Commit f970f64a by Hoang Phuc Do

Add add to cart feature

parent 764def09
...@@ -48,6 +48,10 @@ gem 'rubocop', '~> 0.49.1' ...@@ -48,6 +48,10 @@ gem 'rubocop', '~> 0.49.1'
gem 'activeadmin', '~> 1.0' gem 'activeadmin', '~> 1.0'
# CKEditor is a WYSIWYG editor to be used inside web pages # CKEditor is a WYSIWYG editor to be used inside web pages
gem 'ckeditor', '~> 4.2', '>= 4.2.3' gem 'ckeditor', '~> 4.2', '>= 4.2.3'
# I like font-awesome. I like the asset pipeline. I like semantic versioning. If you do too, you're welcome.
gem 'font-awesome-rails', '~> 4.7', '>= 4.7.0.2'
# Use jquery as the JavaScript library
gem 'jquery-rails'
# Use Capistrano for deployment # Use Capistrano for deployment
# gem 'capistrano-rails', group: :development # gem 'capistrano-rails', group: :development
......
...@@ -108,6 +108,8 @@ GEM ...@@ -108,6 +108,8 @@ GEM
faker (1.6.6) faker (1.6.6)
i18n (~> 0.5) i18n (~> 0.5)
ffi (1.9.18) ffi (1.9.18)
font-awesome-rails (4.7.0.2)
railties (>= 3.2, < 5.2)
formtastic (3.1.5) formtastic (3.1.5)
actionpack (>= 3.2.13) actionpack (>= 3.2.13)
formtastic_i18n (0.6.0) formtastic_i18n (0.6.0)
...@@ -284,7 +286,9 @@ DEPENDENCIES ...@@ -284,7 +286,9 @@ DEPENDENCIES
coffee-rails (~> 4.2) coffee-rails (~> 4.2)
devise (~> 4.3) devise (~> 4.3)
faker (~> 1.6, >= 1.6.3) faker (~> 1.6, >= 1.6.3)
font-awesome-rails (~> 4.7, >= 4.7.0.2)
jbuilder (~> 2.5) jbuilder (~> 2.5)
jquery-rails
kaminari (~> 1.0, >= 1.0.1) kaminari (~> 1.0, >= 1.0.1)
listen (>= 3.0.5, < 3.2) listen (>= 3.0.5, < 3.2)
mini_magick mini_magick
......
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>Copyright (C) 2015 by original authors @ fontello.com</metadata>
<defs>
<font id="minicart-font" horiz-adv-x="1000" >
<font-face font-family="minicart-font" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="850" descent="-150" />
<missing-glyph horiz-adv-x="1000" />
<glyph glyph-name="glyph" unicode="&#xe800;" d="m225 600c0 25 19 45 43 45 25 0 45-20 45-45 0-23-20-43-45-43-24 0-43 20-43 43z m478 0c0 25 20 45 43 45 24 0 43-20 43-45 0-23-19-43-43-43-23 0-43 20-43 43z m-488 121c-65 0-115-55-115-121l-67-566c0-67 51-122 114-122l707 0c62 0 113 55 113 122l-67 566c0 66-50 121-115 121z m553 0c-26 110-135 192-266 192-131 0-240-82-266-192l80 0c24 70 98 123 186 123 88 0 162-53 186-123z" horiz-adv-x="1000" />
</font>
</defs>
</svg>
\ No newline at end of file
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives. // about supported directives.
// //
//= require rails-ujs //= require jquery
//= require jquery_ujs
//# require rails-ujs
//= require turbolinks //= require turbolinks
//# require_tree . //# require_tree .
// "bootstrap-sprockets" must be imported before "bootstrap" and "bootstrap/variables" // "bootstrap-sprockets" must be imported before "bootstrap" and "bootstrap/variables"
@import "bootstrap-sprockets"; @import "bootstrap-sprockets";
@import "bootstrap"; @import "bootstrap";
\ No newline at end of file @import "font-awesome";
\ No newline at end of file
@font-face {
font-family: 'minicart-font';
src: asset_url('minicart-font.eot?v=1.0');
src: asset_url('minicart-font.eot?#iefix&v=1.0') format("embedded-opentype"), asset_url('minicart-font.woff?v=1.0') format("woff"), asset_url('minicart-font.ttf?v=1.0') format("truetype"), asset_url('minicart-font.svg?v=1.0#minicart-font') format("svg");
font-weight: normal;
font-style: normal;
}
/* /*
* Common styles * Common styles
*/ */
...@@ -78,6 +85,100 @@ a { ...@@ -78,6 +85,100 @@ a {
padding-right: 12px; padding-right: 12px;
} }
} }
/*
* Header
*/
#header .header-logo {
float: left;
position: relative;
}
.minicart-icon:before {
font-family: "minicart-font" !important;
font-style: normal !important;
font-weight: normal !important;
font-variant: normal !important;
text-transform: none !important;
speak: none;
line-height: 1;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.minicart-icon:before {
content: "\e800";
}
#header .header-row {
display: table-row;
clear: both;
}
#header .header-column {
display: table-cell;
vertical-align: middle;
}
#header .header-column .row {
margin: 0;
}
#header .header-column .header-row {
display: block;
clear: right;
}
#header .cart-area {
float: right;
vertical-align: middle;
}
@media (max-width: 991px) {
#header .cart-area {
margin-top: 5.5px;
}
}
#header .cart-dropdown {
position: relative;
display: inline-block;
vertical-align: middle;
padding-left: 7px;
padding-right: 7px;
}
#header .cart-dropdown .cart-dropdown-icon {
position: relative;
display: inline-block;
height: 40px;
padding: 0;
line-height: 40px;
text-align: center;
top: -1px;
color: #fff;
text-decoration: none !important;
}
#header .cart-dropdown .cart-dropdown-icon i {
font-size: 35px;
color: #000;
}
#header .cart-dropdown .cart-dropdown-icon .cart-info {
position: absolute;
width: 100%;
text-align: center;
top: 50%;
margin-top: -4px;
left: 0;
padding: 0;
display: block;
line-height: 1;
}
#header .cart-dropdown .cart-dropdown-icon .cart-info .cart-qty {
font-size: 14px;
font-weight: 600;
}
/*
* Button
*/
.btn.btn-default.hover-primary:hover, .btn.btn-default.hover-primary:focus {
color: #fff;
background-color: #000;
border-color: #000;
}
/* /*
* Products Grid * Products Grid
...@@ -731,4 +832,218 @@ html .featured-box-primary .box-content { ...@@ -731,4 +832,218 @@ html .featured-box-primary .box-content {
/* Featured Box Left */ /* Featured Box Left */
.featured-box-text-left { .featured-box-text-left {
text-align: left; text-align: left;
}
/*
* Product details
*/
.product-details-box .product-actions {
margin-top: 10px;
padding-bottom: 10px;
margin-bottom: 20px;
border-bottom: 1px solid #ebebeb;
}
.product-details-box .product-actions:after {
content: '';
display: table;
clear: both;
}
.product-details-box .product-actions .addtocart {
color: #fff;
border-color: #000;
background-color: #000;
line-height: 36px;
height: 38px;
min-width: 160px;
text-align: center;
}
.product-details-box .product-actions .addtocart:hover, .product-details-box .product-actions .addtocart:focus {
color: #fff;
border-color: #0f0f0f;
background-color: #0f0f0f;
}
/*
* Cart table
*/
.cart-table-wrap {
border: 1px solid #ececec;
border-radius: 0;
background: #fff;
display: block;
padding: 30px;
margin-bottom: 50px;
box-shadow: 0 2px 3px rgba(0, 0, 0, 0.08);
}
@media (min-width: 992px) {
.cart-table-wrap {
margin-bottom: 60px;
}
}
.cart-table {
width: 100%;
border: 0;
border-spacing: 0;
font-size: 14px;
}
.cart-table thead tr {
border-bottom: 1px solid #dcdcdc;
}
.cart-table thead tr th {
font-weight: 600;
padding: 15px 10px;
color: #777;
white-space: nowrap;
vertical-align: middle;
line-height: 1;
}
.cart-table tbody tr td {
border-bottom: 1px solid #dcdcdc;
padding: 15px 10px;
line-height: 1.3;
}
.cart-table tbody tr td.product-action-td {
padding-left: 0;
padding-right: 0;
}
.cart-table tbody tr td.product-image-td a {
display: block;
}
.cart-table tbody tr td.product-image-td a img {
display: block;
width: 100px;
height: auto;
}
.cart-table tbody tr td.product-name-td h2 {
font-size: 14px;
font-weight: 400;
margin-bottom: 0;
}
.cart-table tbody tr td.product-name-td h2 a {
color: #000;
}
.cart-table tbody tr td.product-name-td h2 a:hover, .cart-table tbody tr td.product-name-td h2 a:focus {
color: #000;
}
.cart-table tfoot td {
padding: 15px 5px 0;
}
.cart-table tfoot .btn.btn-default.btn-continue {
float: left;
}
.cart-table tfoot .btn.btn-default.btn-update {
float: right;
margin-left: 10px;
}
.cart-table tfoot .btn.btn-default.btn-clear {
float: right;
}
@media (max-width: 1199px) {
.cart-table thead {
display: none;
}
.cart-table tbody tr {
position: relative;
display: block;
border-bottom: 1px solid #dcdcdc;
padding: 25px 0;
}
.cart-table tbody tr td {
display: block;
padding: 0 0 15px;
width: 100%;
border-width: 0;
text-align: center !important;
}
.cart-table tbody tr td:last-child {
padding-bottom: 0;
}
.cart-table tbody tr td .qty-holder {
width: 90px;
}
.cart-table tbody tr td.product-action-td {
position: absolute;
top: 20px;
z-index: 1;
}
.cart-table tbody tr td.product-action-td .btn-remove {
float: right;
}
.cart-table tbody tr td.product-image-td {
padding-bottom: 15px;
}
.cart-table tbody tr td.product-image-td a img {
margin: 0 auto;
}
.cart-table tbody tr:first-child {
padding-top: 0;
}
.cart-table tbody tr:first-child td.product-action-td {
top: -5px;
}
.cart-table tfoot td {
padding-top: 25px;
}
.cart-table tfoot .btn.btn-default.btn-continue, .cart-table tfoot .btn.btn-default.btn-update, .cart-table tfoot .btn.btn-default.btn-clear {
float: none;
display: block;
width: 100%;
margin: 0 0 10px;
}
}
.cart .sidebar.shop-sidebar .panel .btn-block + .btn-block {
margin-top: 0;
}
.cart .sidebar.shop-sidebar .panel .btn-link {
font-size: 13px;
}
/*
* Totals table
*/
.totals-table {
width: 100%;
margin-bottom: 5px;
}
.totals-table tbody tr {
border-bottom: 1px solid #dcdcdc;
}
.totals-table tbody tr:last-child {
border-bottom: none;
}
.totals-table tbody tr td {
padding: 10px;
line-height: 1.4;
font-size: 15px;
font-weight: 300;
text-align: left !important;
}
.totals-table tbody tr td:last-child {
color: #000;
text-align: right !important;
font-weight: 600;
}
.totals-table tbody tr:last-child td:last-child {
font-size: 17px;
}
/*
* Checkout
*/
.form-col {
color: #393939;
margin-bottom: 30px;
}
.form-col .checkout-review-action {
border-top: 1px solid #b6b6b6;
}
.form-col .checkout-review-action h5 {
color: #3f3f3f;
font-size: 16px;
font-weight: bold;
margin-top: 30px;
margin-bottom: 10px;
}
.form-col .checkout-review-action h5 span {
margin-left: 45px;
} }
\ No newline at end of file
body { body {
font-size: 13px; font-size: 13px;
line-height: 1.5; font-family: "Open Sans", Arial, sans-serif;
background: image-url('shop-14-bg.png') top center repeat-x; color: #777;
line-height: 1.5;
background: transparent image-url('shop-14-bg.png') repeat-x top center;
} }
a { a {
color: #000000; color: #000000;
} }
a:hover { a:hover {
color: #0d0d0d; color: #0d0d0d;
} }
a:focus { a:focus {
color: #0d0d0d; color: #0d0d0d;
} }
a:active { a:active {
color: #000000; color: #000000;
} }
.pagination > li > a, .pagination > li > a,
.pagination > li > span, .pagination > li > span,
...@@ -21,7 +23,7 @@ a:active { ...@@ -21,7 +23,7 @@ a:active {
.pagination > li > span:hover, .pagination > li > span:hover,
.pagination > li > a:focus, .pagination > li > a:focus,
.pagination > li > span:focus { .pagination > li > span:focus {
color: #000000; color: #000000;
} }
.pagination > .active > a, .pagination > .active > a,
.pagination > .active > span, .pagination > .active > span,
...@@ -29,98 +31,98 @@ a:active { ...@@ -29,98 +31,98 @@ a:active {
.pagination > .active > span:hover, .pagination > .active > span:hover,
.pagination > .active > a:focus, .pagination > .active > a:focus,
.pagination > .active > span:focus { .pagination > .active > span:focus {
background-color: #000000 !important; background-color: #000000 !important;
border-color: #000000; border-color: #000000;
} }
html .btn-primary { html .btn-primary {
color: #ffffff; color: #ffffff;
background-color: #000000; background-color: #000000;
border-color: #000000 #000000 #000000; border-color: #000000 #000000 #000000;
} }
html .btn-primary:hover { html .btn-primary:hover {
border-color: #0d0d0d #0d0d0d #000000; border-color: #0d0d0d #0d0d0d #000000;
background-color: #0d0d0d; background-color: #0d0d0d;
} }
html .btn-primary:active, html .btn-primary:active,
html .btn-primary:focus, html .btn-primary:focus,
html .btn-primary:active:hover, html .btn-primary:active:hover,
html .btn-primary:active:focus { html .btn-primary:active:focus {
border-color: #000000 #000000 #000000; border-color: #000000 #000000 #000000;
background-color: #000000; background-color: #000000;
} }
html .btn-primary.dropdown-toggle { html .btn-primary.dropdown-toggle {
border-left-color: #000000; border-left-color: #000000;
} }
html .btn-primary[disabled], html .btn-primary[disabled],
html .btn-primary[disabled]:hover, html .btn-primary[disabled]:hover,
html .btn-primary[disabled]:active, html .btn-primary[disabled]:active,
html .btn-primary[disabled]:focus { html .btn-primary[disabled]:focus {
border-color: #333333; border-color: #333333;
background-color: #333333; background-color: #333333;
} }
html .btn-primary:hover, html .btn-primary:hover,
html .btn-primary:focus, html .btn-primary:focus,
html .btn-primary:active:hover, html .btn-primary:active:hover,
html .btn-primary:active:focus { html .btn-primary:active:focus {
color: #ffffff; color: #ffffff;
} }
.btn { .btn {
border-radius: 0; border-radius: 0;
} }
.featured-box .box-content { .featured-box .box-content {
border-radius: 0; border-radius: 0;
} }
.product-essential { .product-essential {
margin-bottom: 50px; margin-bottom: 50px;
} }
@media (max-width: 767px) { @media (max-width: 767px) {
.product-img-box { .product-img-box {
margin-bottom: 25px; margin-bottom: 25px;
} }
} }
.product-img-box img { .product-img-box img {
display: block; display: block;
width: 100%; width: 100%;
height: auto; height: auto;
} }
.product-img-box .product-img-wrapper { .product-img-box .product-img-wrapper {
padding: 3px; padding: 3px;
border: 1px solid #ddd; border: 1px solid #ddd;
border-radius: 0; border-radius: 0;
} }
.product-img-box-wrapper { .product-img-box-wrapper {
position: relative; position: relative;
margin-bottom: 10px; margin-bottom: 10px;
} }
.product-details-box .product-name { .product-details-box .product-name {
margin: 15px 0; margin: 15px 0;
font-size: 28px; font-size: 28px;
font-weight: 600; font-weight: 600;
line-height: 1; line-height: 1;
color: #555; color: #555;
} }
.product-details-box .product-short-desc { .product-details-box .product-short-desc {
padding: 0 0 10px; padding: 0 0 10px;
border-bottom: 1px solid #ebebeb; border-bottom: 1px solid #ebebeb;
} }
.product-details-box .product-short-desc p { .product-details-box .product-short-desc p {
font-size: 14px; font-size: 14px;
line-height: 1.65; line-height: 1.65;
margin: 0 0 20px; margin: 0 0 20px;
} }
.product-details-box .product-detail-info { .product-details-box .product-detail-info {
padding-bottom: 20px; padding-bottom: 20px;
margin-top: 20px; margin-top: 20px;
border-bottom: 1px solid #ebebeb; border-bottom: 1px solid #ebebeb;
} }
.product-details-box .product-detail-info .product-price-box { .product-details-box .product-detail-info .product-price-box {
margin: 0 0 20px; margin: 0 0 20px;
} }
.product-details-box .product-detail-info .product-price-box .product-price { .product-details-box .product-detail-info .product-price-box .product-price {
font-size: 33px; font-size: 33px;
line-height: 1; line-height: 1;
color: #000; color: #000;
} }
#header .header-body { #header .header-body {
border-top: none; border-top: none;
...@@ -139,6 +141,8 @@ html .btn-primary:active:focus { ...@@ -139,6 +141,8 @@ html .btn-primary:active:focus {
#header .header-container { #header .header-container {
padding-top: 28px; padding-top: 28px;
padding-bottom: 28px; padding-bottom: 28px;
position: relative;
display: table;
} }
#header .header-container.header-nav { #header .header-container.header-nav {
padding: 0; padding: 0;
......
class ApplicationController < ActionController::Base class ApplicationController < ActionController::Base
protect_from_forgery with: :exception protect_from_forgery with: :exception
include CartsHelper
before_action :set_cart
def authenticate_active_admin_user! def authenticate_active_admin_user!
authenticate_user! authenticate_user!
......
class CartsController < ApplicationController
include CartsHelper
before_action :set_cart
rescue_from ActiveRecord::RecordNotFound, with: :invalid_cart
def index
@line_items = {}
@cart.each do |product_id, product_attrs|
@line_items[product_id] = { product: Product.find(product_id),
quantity: product_attrs['quantity'] }
end
end
def delete_line_item
product_id = params[:id]
if @cart.has_key? product_id
@cart.delete(product_id)
session[:cart] = @cart
product_title = Product.select(:title).find(product_id).title
respond_to do |format|
format.html { redirect_to cart_index_url, notice: "#{product_title} was sucessfully removed from your cart" }
end
end
end
def destroy
session[:cart] = nil
respond_to do |format|
format.html { redirect_to root_url }
format.json { head :no_content }
end
end
private
def invalid_cart
redirect_to root_url
end
end
\ No newline at end of file
class LineItemsController < ApplicationController
include CartsHelper
before_action :set_cart, only: [:create]
before_action :set_line_item, only: [:show, :edit, :update, :destroy]
def create
product = Product.find(params[:product_id])
@line_items = add_to_cart(product, 1)
respond_to do |format|
format.html { redirect_to cart_index_url, notice: "#{product.title} was sucessfully added to your cart" }
format.js
format.json { render 'show' }
end
end
def destroy
@line_item.destroy
respond_to do |format|
format.html { redirect_to cart_index_url, notice: "#{product.title} was sucessfully removed from your cart" }
format.json { head :no_content }
end
end
private
def set_line_item
@line_item = LineItem.find(params[:id])
end
def line_item_params
params.require(:line_item).permit(:product_id)
end
end
\ No newline at end of file
class OrdersController < ApplicationController
include CartsHelper
before_action :authenticate_user!, only: :new
before_action :set_cart, only: [:new, :create]
before_action :ensure_cart_is_not_empty, only: :new
def new
@order = Order.new
end
def create
@order = current_user.orders.create
create_line_items_for_order(@order)
respond_to do |format|
if @order.save
destroy_cart_session
format.html { redirect_to root_url }
format.json { render :show }
else
format.html { redirect_to root_url }
format.json { render json: @order.errors }
end
end
end
def create_line_items_for_order(order)
@cart.each do |product_id, product_attrs|
order.line_items.create(product_id: product_id, quantity: product_attrs['quantity'])
end
end
private
def ensure_cart_is_not_empty
redirect_to root_url if @cart.empty?
end
end
\ No newline at end of file
module CartsHelper
def set_cart
return @cart = session[:cart] unless session[:cart].nil?
session[:cart] = {}
@cart = session[:cart]
end
def add_to_cart(product, quantity)
cart_key = product.id
if @cart.has_key? cart_key.to_s
@cart[cart_key.to_s]['quantity'] += quantity
else
@cart[cart_key] = { quantity: quantity }
end
end
def destroy_cart_session
session[:cart] = nil
end
def cart_total_price
total_price = 0
@cart.each do |product_id, product_attrs|
product_price = Product.select(:price).find(product_id).price
total_price += (product_price * product_attrs['quantity'])
end
total_price
end
def cart_total_items
if @cart.nil?
total = 0
else
total = session[:cart].sum { |product_id, product_attrs| product_attrs['quantity'] || product_attrs[:quantity] }
end
total
end
end
class LineItem < ApplicationRecord
belongs_to :product, optional: true
belongs_to :order, optional: true
def create_line_items_from_cart(cart)
cart.each do |product_id, product_attrs|
LineItem.create(product_id: product_id,
quantity: product_attrs['quantity'])
end
end
def total_price
product.price * quantity
end
end
\ No newline at end of file
class Order < ApplicationRecord
has_many :line_items, dependent: :destroy
belongs_to :user
def add_line_items_from_cart(cart)
cart.each do |product_id, product_attrs|
line_items.create
end
end
end
\ No newline at end of file
...@@ -4,4 +4,5 @@ class User < ApplicationRecord ...@@ -4,4 +4,5 @@ class User < ApplicationRecord
devise :database_authenticatable, :registerable, devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable :recoverable, :rememberable, :trackable, :validatable
has_many :products, dependent: :destroy has_many :products, dependent: :destroy
has_many :orders, dependent: :destroy
end end
<%= cart_total_items %>
\ No newline at end of file
<div class="cart">
<div class="container">
<h1 class="h2 heading-primary mt-lg clearfix">
Shopping Cart
</h1>
<div class="row">
<div class="col-md-8 col-lg-9">
<div class="cart-table-wrap">
<% if notice.presence %>
<div class="alert alert-success"><%= notice %></div>
<% end %>
<table class="cart-table">
<thead>
<tr>
<th></th>
<th></th>
<th>Product Name</th>
<th>Unit Price</th>
<th>Qty</th>
<th>Subtotal</th>
</tr>
</thead>
<tbody>
<% @line_items.each do |id, line_item| %>
<tr>
<td class="product-action-td">
<%= link_to fa_icon('times'), remove_line_item_cart_path(id), method: :delete, data: { confirm: 'Are you sure?' } %>
</td>
<td class="product-image-td"></td>
<td class="product-name-td">
<h2 class="product-name">
<%= link_to line_item[:product].title, product_url(line_item[:product]) %>
</h2>
</td>
<td><%= number_to_currency(line_item[:product].price) %></td>
<td><%= line_item[:quantity] %></td>
<td><%= number_to_currency(line_item[:product].price * line_item[:quantity]) %></td>
</tr>
<% end %>
</tbody>
<tfoot>
<tr>
<td colspan="6">
<%= button_to 'Clear Shopping Cart', remove_cart_path, method: :delete, data: { confirm: 'Are you sure?' }, class: 'btn btn-default hover-primary btn-clear' %>
</td>
</tr>
</tfoot>
</table>
</div>
</div>
<aside class="col-md-4 col-lg-3 sidebar shop-sidebar">
<div class="panel-group">
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">Cart Totals</h4>
</div>
<div id="panel-cart-total">
<div class="panel-body">
<table class="totals-table">
<tbody>
<tr>
<td>Grand Total</td>
<td><%= number_to_currency(cart_total_price) %></td>
</tr>
</tbody>
</table>
<div class="totals-table-action">
<%= button_to 'Proceed to checkout', new_order_path, method: :get, class: 'btn btn-primary btn-block' %>
</div>
</div>
</div>
</div>
</div>
</aside>
</div>
</div>
</div>
\ No newline at end of file
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %> <%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
<div class="form-content"> <div class="form-content">
<h3 class="heading-text-color font-weight-normal"> <h3 class="heading-text-color font-weight-normal">
Registered Customers
</h3> </h3>
<div class="form-group"> <div class="form-group">
......
...@@ -9,17 +9,3 @@ ...@@ -9,17 +9,3 @@
<%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %> <%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %>
<%= link_to "Forgot your password?", new_password_path(resource_name) %><br /> <%= link_to "Forgot your password?", new_password_path(resource_name) %><br />
<% end -%> <% end -%>
<%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
<%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %><br />
<% end -%>
<%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
<%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %><br />
<% end -%>
<%- if devise_mapping.omniauthable? %>
<%- resource_class.omniauth_providers.each do |provider| %>
<%= link_to "Sign in with #{OmniAuth::Utils.camelize(provider)}", omniauth_authorize_path(resource_name, provider) %><br />
<% end -%>
<% end -%>
...@@ -14,9 +14,16 @@ ...@@ -14,9 +14,16 @@
</div> </div>
</div> </div>
<div class="header-column"> <div class="header-column">
<div class="header-row"> <div class="row">
<div class="cart-area"> <div class="cart-area">
<div class="cart-dropdown">
<a href="<%= cart_index_url %>" class="cart-dropdown-icon">
<i class="minicart-icon"></i>
<span class="cart-info">
<span class="cart-qty"><%= cart_total_items %></span>
</span>
</a>
</div>
</div> </div>
<div class="header-search"> <div class="header-search">
......
<tr>
<td class="product-action-td">
<%= link_to fa_icon('times'), line_item, method: :delete, data: { confirm: 'Are you sure?' } %>
</td>
<td class="product-image-td"></td>
<td class="product-name-td">
<h2 class="product-name">
<%= link_to line_item.product.title, product_url(line_item.product) %>
</h2>
</td>
<td><%= number_to_currency(line_item.product.price) %></td>
<td><%= line_item.quantity %></td>
<td><%= number_to_currency(line_item.total_price) %></td>
</tr>
\ No newline at end of file
$('.cart-qty').html("<%= render 'carts/cart_quantity' %>");
\ No newline at end of file
<div class="checkout">
<div class="container">
<h1 class="h2 heading-primary mt-lg mb-md clearfix">
Checkout
</h1>
<div class="row">
<div class="col-md-12">
<div class="form-col">
<h3>Your Information</h3>
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<label for="email">Email:</label>
<input type="text" class="form-control" value="<%= current_user.email %>" disabled>
</div>
</div>
</div>
</div>
<div class="form-col">
<div class="checkout-review-action">
<h5>Grand Total <span><%= number_to_currency(cart_total_price) %></span></h5>
<%= form_for @order do |f| %>
<%= f.submit 'Place Order now', class: 'btn btn-primary' %>
<% end %>
</div>
</div>
</div>
</div>
</div>
</div>
\ No newline at end of file
...@@ -21,6 +21,11 @@ ...@@ -21,6 +21,11 @@
<span class="product-price"><%= number_to_currency(@product.price) %></span> <span class="product-price"><%= number_to_currency(@product.price) %></span>
</div> </div>
</div> </div>
<div class="product-actions">
<div class="product-detail-qty">
</div>
<%= button_to 'Add to cart', line_items_path(product_id: @product), remote: true, class: 'addtocart' %>
</div>
</div> </div>
</div> </div>
</div> </div>
......
...@@ -14,5 +14,6 @@ module DhpVenshop ...@@ -14,5 +14,6 @@ module DhpVenshop
# Settings in config/environments/* take precedence over those specified here. # Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers # Application configuration should go into files in config/initializers
# -- all .rb files in that directory are automatically loaded. # -- all .rb files in that directory are automatically loaded.
config.assets.paths << Rails.root.join('app', 'assets', 'fonts')
end end
end end
...@@ -3,6 +3,11 @@ Rails.application.routes.draw do ...@@ -3,6 +3,11 @@ Rails.application.routes.draw do
root 'static_pages#index' root 'static_pages#index'
resources :categories resources :categories
resources :products resources :products
resources :orders, only: [:new, :create, :show]
resources :line_items
resources :cart, only: :index, controller: 'carts'
delete '/cart/remove/line_item/:id', to: 'carts#delete_line_item', as: 'remove_line_item_cart'
delete '/cart/remove', to: 'carts#destroy', as: 'remove_cart'
devise_for :users devise_for :users
ActiveAdmin.routes(self) ActiveAdmin.routes(self)
mount Ckeditor::Engine => '/ckeditor' mount Ckeditor::Engine => '/ckeditor'
......
class CreateLineItems < ActiveRecord::Migration[5.1]
def change
create_table :line_items do |t|
t.integer :quantity, default: 1
t.references :product, index: true
t.references :order, index: true
t.timestamps
end
end
end
class CreateOrders < ActiveRecord::Migration[5.1]
def change
create_table :orders do |t|
t.references :user, index: true
t.timestamps
end
end
end
class AddCustomFieldsToUsers < ActiveRecord::Migration[5.1]
def change
add_column :users, :username, :string
add_column :users, :first_name, :string
add_column :users, :last_name, :string
end
end
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20170609152744) do ActiveRecord::Schema.define(version: 20170613012113) do
create_table "active_admin_comments", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t| create_table "active_admin_comments", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
t.string "namespace" t.string "namespace"
...@@ -43,6 +43,23 @@ ActiveRecord::Schema.define(version: 20170609152744) do ...@@ -43,6 +43,23 @@ ActiveRecord::Schema.define(version: 20170609152744) do
t.index ["type"], name: "index_ckeditor_assets_on_type" t.index ["type"], name: "index_ckeditor_assets_on_type"
end end
create_table "line_items", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
t.integer "quantity", default: 1
t.bigint "product_id"
t.bigint "order_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["order_id"], name: "index_line_items_on_order_id"
t.index ["product_id"], name: "index_line_items_on_product_id"
end
create_table "orders", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
t.bigint "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["user_id"], name: "index_orders_on_user_id"
end
create_table "products", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t| create_table "products", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
t.string "title" t.string "title"
t.text "description" t.text "description"
...@@ -71,6 +88,9 @@ ActiveRecord::Schema.define(version: 20170609152744) do ...@@ -71,6 +88,9 @@ ActiveRecord::Schema.define(version: 20170609152744) do
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.boolean "super_admin", default: false, null: false t.boolean "super_admin", default: false, null: false
t.string "username"
t.string "first_name"
t.string "last_name"
t.index ["email"], name: "index_users_on_email", unique: true t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end end
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment