Commit 92e3a257 by Trong Huu Nguyen

Merge branch 'dhp_user_management' into 'development'

[REVIEW] User management

See merge request !2
parents 7099ab3b 68223ff6
...@@ -17,3 +17,9 @@ ...@@ -17,3 +17,9 @@
/yarn-error.log /yarn-error.log
.byebug_history .byebug_history
# Ignore public/uploads folder
/public/uploads/*
# Ignore database config file
/config/database.yml
\ No newline at end of file
...@@ -40,6 +40,10 @@ gem 'bootstrap-sass', '~> 3.3.6' ...@@ -40,6 +40,10 @@ gem 'bootstrap-sass', '~> 3.3.6'
gem 'faker', '~> 1.6', '>= 1.6.3' gem 'faker', '~> 1.6', '>= 1.6.3'
# Kaminari is a Scope & Engine based, clean, powerful, agnostic, customizable and sophisticated paginator for Rails 4+ # Kaminari is a Scope & Engine based, clean, powerful, agnostic, customizable and sophisticated paginator for Rails 4+
gem 'kaminari', '~> 1.0', '>= 1.0.1' gem 'kaminari', '~> 1.0', '>= 1.0.1'
# Flexible authentication solution for Rails with Warden
gem 'devise', '~> 4.3'
# Automatic Ruby code style checking tool. Aims to enforce the community-driven Ruby Style Guide.
gem 'rubocop', '~> 0.49.1'
# Use Capistrano for deployment # Use Capistrano for deployment
# gem 'capistrano-rails', group: :development # gem 'capistrano-rails', group: :development
......
...@@ -41,8 +41,10 @@ GEM ...@@ -41,8 +41,10 @@ GEM
addressable (2.5.1) addressable (2.5.1)
public_suffix (~> 2.0, >= 2.0.2) public_suffix (~> 2.0, >= 2.0.2)
arel (8.0.0) arel (8.0.0)
ast (2.3.0)
autoprefixer-rails (7.1.1) autoprefixer-rails (7.1.1)
execjs execjs
bcrypt (3.1.11)
bindex (0.5.0) bindex (0.5.0)
bootstrap-sass (3.3.6) bootstrap-sass (3.3.6)
autoprefixer-rails (>= 5.2.1) autoprefixer-rails (>= 5.2.1)
...@@ -70,6 +72,12 @@ GEM ...@@ -70,6 +72,12 @@ GEM
execjs execjs
coffee-script-source (1.12.2) coffee-script-source (1.12.2)
concurrent-ruby (1.0.5) concurrent-ruby (1.0.5)
devise (4.3.0)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 4.1.0, < 5.2)
responders
warden (~> 1.2.3)
erubi (1.6.0) erubi (1.6.0)
execjs (2.7.0) execjs (2.7.0)
faker (1.6.6) faker (1.6.6)
...@@ -113,6 +121,11 @@ GEM ...@@ -113,6 +121,11 @@ GEM
nio4r (2.1.0) nio4r (2.1.0)
nokogiri (1.8.0) nokogiri (1.8.0)
mini_portile2 (~> 2.2.0) mini_portile2 (~> 2.2.0)
orm_adapter (0.5.0)
parallel (1.11.2)
parser (2.4.0.0)
ast (~> 2.2)
powerpack (0.1.1)
public_suffix (2.0.5) public_suffix (2.0.5)
puma (3.9.1) puma (3.9.1)
rack (2.0.3) rack (2.0.3)
...@@ -141,10 +154,23 @@ GEM ...@@ -141,10 +154,23 @@ GEM
method_source method_source
rake (>= 0.8.7) rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0) thor (>= 0.18.1, < 2.0)
rainbow (2.2.2)
rake
rake (12.0.0) rake (12.0.0)
rb-fsevent (0.9.8) rb-fsevent (0.9.8)
rb-inotify (0.9.8) rb-inotify (0.9.8)
ffi (>= 0.5.0) ffi (>= 0.5.0)
responders (2.4.0)
actionpack (>= 4.2.0, < 5.3)
railties (>= 4.2.0, < 5.3)
rubocop (0.49.1)
parallel (~> 1.10)
parser (>= 2.3.3.1, < 3.0)
powerpack (~> 0.1)
rainbow (>= 1.99.1, < 3.0)
ruby-progressbar (~> 1.7)
unicode-display_width (~> 1.0, >= 1.0.1)
ruby-progressbar (1.8.1)
ruby_dep (1.5.0) ruby_dep (1.5.0)
rubyzip (1.2.1) rubyzip (1.2.1)
sass (3.4.24) sass (3.4.24)
...@@ -180,6 +206,9 @@ GEM ...@@ -180,6 +206,9 @@ GEM
thread_safe (~> 0.1) thread_safe (~> 0.1)
uglifier (3.2.0) uglifier (3.2.0)
execjs (>= 0.3.0, < 3) execjs (>= 0.3.0, < 3)
unicode-display_width (1.2.1)
warden (1.2.7)
rack (>= 1.0)
web-console (3.5.1) web-console (3.5.1)
actionview (>= 5.0) actionview (>= 5.0)
activemodel (>= 5.0) activemodel (>= 5.0)
...@@ -201,6 +230,7 @@ DEPENDENCIES ...@@ -201,6 +230,7 @@ DEPENDENCIES
capybara (~> 2.13) capybara (~> 2.13)
carrierwave (~> 1.0) carrierwave (~> 1.0)
coffee-rails (~> 4.2) coffee-rails (~> 4.2)
devise (~> 4.3)
faker (~> 1.6, >= 1.6.3) faker (~> 1.6, >= 1.6.3)
jbuilder (~> 2.5) jbuilder (~> 2.5)
kaminari (~> 1.0, >= 1.0.1) kaminari (~> 1.0, >= 1.0.1)
...@@ -209,6 +239,7 @@ DEPENDENCIES ...@@ -209,6 +239,7 @@ DEPENDENCIES
mysql2 (>= 0.3.18, < 0.5) mysql2 (>= 0.3.18, < 0.5)
puma (~> 3.7) puma (~> 3.7)
rails (~> 5.1.1) rails (~> 5.1.1)
rubocop (~> 0.49.1)
sass-rails (~> 5.0) sass-rails (~> 5.0)
selenium-webdriver selenium-webdriver
spring spring
......
...@@ -12,4 +12,4 @@ ...@@ -12,4 +12,4 @@
// //
//= require rails-ujs //= require rails-ujs
//= require turbolinks //= require turbolinks
//= require_tree . //# require_tree .
...@@ -10,6 +10,9 @@ ...@@ -10,6 +10,9 @@
* files in this directory. Styles in this file should be added after the last require_* statement. * files in this directory. Styles in this file should be added after the last require_* statement.
* It is generally better to create a new file per style scope. * It is generally better to create a new file per style scope.
* *
*= require_tree . *# require_tree .
*= require_self *= require_self
*= require custom
*= require main_style
*= require skin_14
*/ */
/****************************************** /*
1. Common styles * Common styles
******************************************/ */
body { body {
background: #f7f7f7; font-size: 13px;
color: #333333; line-height: 1.5;
font-family: 'Poppins', Arial, Helvetica, sans-serif;
font-size: 12px;
font-weight: 400;
line-height: 15px;
text-align: left;
letter-spacing: 0.8px;
}
* {
margin: 0;
padding: 0
}
img {
border: 0;
max-width: 100%
}
a {
color: #333333;
text-decoration: none;
transition: 0.5s all ease
} }
a:hover {
text-decoration: none; h1, h2, h3, h4, h5, h6,
color: #ffd401 .h1, .h2, .h3, .h4, .h5, .h6 {
} color: #313131;
a:focus {
outline: 0;
text-decoration: none;
color: #666
}
:focus {
outline: none;
}
h1, h2, h3, h4, h5, h6 {
margin: 0 0 5px;
line-height: 1.35; line-height: 1.35;
color: #333333 margin: 0 0 15px;
} text-transform: none;
h1 { letter-spacing: 0 !important;
font-weight: normal
}
h2 {
font-weight: normal;
margin-bottom: 15px
}
h3 {
font-weight: 500
}
h4 {
font-weight: 500
}
h5 {
font-weight: 500
}
h6 {
font-weight: 500
} }
form {
display: inline h1,
.h1 {
font-size: 22px;
margin-bottom: 20px;
} }
fieldset {
border: 0 h2,
.h2 {
font-size: 20px;
margin-bottom: 20px;
} }
legend {
display: none h3,
.h3 {
font-size: 18px;
} }
table {
border: 0; h4,
border-spacing: 0; .h4 {
empty-cells: show; font-size: 16px;
font-size: 100%; background-color:#fff;
} }
caption, th, td {
vertical-align: top; h5,
text-align: left; .h5 {
font-weight: normal;background-color:#fff; font-size: 14px;
} }
thead tr th {
font-weight: bold; h6,
text-transform: uppercase; .h6 {
font-size: 12px font-size: 12px;
} }
p { p {
margin: 0 0 10px line-height: 1.5;
}
strong {
font-weight: 600
}
address {
font-style: normal;
line-height: 1.35
}
cite {
font-style: normal
} }
q, blockquote {
quotes: none a {
-webkit-transition: all 0.3s;
-moz-transition: all 0.3s;
transition: all 0.3s;
} }
q:before, q:after {
content: '' @media (min-width: 1320px) {
.container {
width: 1280px;
}
} }
small, big { @media (min-width: 992px) {
font-size: 1em .col-md-9 {
padding-right: 12px;
}
.col-md-9.col-md-push-3 {
padding-right: 15px;
padding-left: 12px;
}
.col-md-9 + .col-md-3 {
padding-left: 12px;
}
.col-md-9 + .col-md-3.col-md-pull-9 {
padding-left: 15px;
padding-right: 12px;
}
} }
ul, ol {
/*
* Products Grid
*/
.products-grid {
list-style: none;
padding: 0; padding: 0;
margin: 0 0 10px 25px margin: 0 -10px 0;
} }
ul ul, ul ol, ol ol, ol ul { .products-grid:after {
margin-bottom: 0 content: '';
display: table;
clear: both;
} }
ul.unstyled, ol.unstyled { .products-grid li {
margin-left: 0; width: 100%;
list-style: none padding: 10px 10px 0;
} }
ul.inline, ol.inline { .products-grid.columns6 {
margin-left: 0; margin: 0 -5px;
list-style: none
} }
ul.inline>li, ol.inline>li { .products-grid.columns6 li {
display: inline-block;
*display:inline;
padding-right: 5px;
padding-left: 5px; padding-left: 5px;
*zoom:1 padding-right: 5px;
}
ul ul, ol ul {
list-style-type: circle
}
.hidden {
display: block !important;
border: 0 !important;
margin: 0 !important;
padding: 0 !important;
font-size: 0 !important;
line-height: 0 !important;
width: 0 !important;
height: 0 !important;
overflow: hidden !important
}
.nobr {
white-space: nowrap !important
}
.wrap {
white-space: normal !important
}
.a-left {
text-align: left !important
}
.a-center {
text-align: center !important
}
.a-right {
text-align: right !important
}
.v-top {
vertical-align: top
}
.v-middle {
vertical-align: middle
} }
.f-left, .left { .products-grid.columns7 {
float: left !important margin: 0 -4px;
} }
.f-right, .right { .products-grid.columns7 li {
float: right !important padding-left: 4px;
padding-right: 4px;
} }
.f-none { .products-grid.columns8 {
float: none !important margin: 0 -3px;
} }
.f-fix { .products-grid.columns8 li {
float: left; padding-left: 3px;
width: 100% padding-right: 3px;
} }
.no-display { @media (min-width: 480px) {
display: none .products-grid li {
float: left;
width: 50%;
}
.products-grid li:nth-child(2n+1) {
clear: left;
}
.products-grid.columns5 li, .products-grid.columns6 li, .products-grid.columns7 li, .products-grid.columns8 li {
width: 33.33%;
}
.products-grid.columns5 li:nth-child(2n+1), .products-grid.columns6 li:nth-child(2n+1), .products-grid.columns7 li:nth-child(2n+1), .products-grid.columns8 li:nth-child(2n+1) {
clear: none;
}
.products-grid.columns5 li:nth-child(3n+1), .products-grid.columns6 li:nth-child(3n+1), .products-grid.columns7 li:nth-child(3n+1), .products-grid.columns8 li:nth-child(3n+1) {
clear: left;
}
} }
.no-margin { @media (min-width: 768px) {
margin: 0 !important .products-grid.columns3 li, .products-grid.columns4 li {
width: 33.33%;
}
.products-grid.columns3 li:nth-child(2n+1), .products-grid.columns4 li:nth-child(2n+1) {
clear: none;
}
.products-grid.columns3 li:nth-child(3n+1), .products-grid.columns4 li:nth-child(3n+1) {
clear: left;
}
.products-grid.columns5 li, .products-grid.columns6 li, .products-grid.columns7 li, .products-grid.columns8 li {
width: 25%;
}
.products-grid.columns5 li:nth-child(3n+1), .products-grid.columns6 li:nth-child(3n+1), .products-grid.columns7 li:nth-child(3n+1), .products-grid.columns8 li:nth-child(3n+1) {
clear: none;
}
.products-grid.columns5 li:nth-child(4n+1), .products-grid.columns6 li:nth-child(4n+1), .products-grid.columns7 li:nth-child(4n+1), .products-grid.columns8 li:nth-child(4n+1) {
clear: left;
}
} }
.no-padding { @media (min-width: 992px) {
padding: 0 !important .products-grid.columns6 li, .products-grid.columns7 li, .products-grid.columns8 li {
width: 20%;
}
.products-grid.columns6 li:nth-child(4n+1), .products-grid.columns7 li:nth-child(4n+1), .products-grid.columns8 li:nth-child(4n+1) {
clear: none;
}
.products-grid.columns6 li:nth-child(5n+1), .products-grid.columns7 li:nth-child(5n+1), .products-grid.columns8 li:nth-child(5n+1) {
clear: left;
}
} }
.no-bg { @media (min-width: 1200px) {
background: none !important .products-grid.columns4 li {
width: 25%;
}
.products-grid.columns4 li:nth-child(3n+1) {
clear: none;
}
.products-grid.columns4 li:nth-child(4n+1) {
clear: left;
}
.products-grid.columns5 li {
width: 20%;
}
.products-grid.columns5 li:nth-child(4n+1) {
clear: none;
}
.products-grid.columns5 li:nth-child(5n+1) {
clear: left;
}
.products-grid.columns6 li {
width: 16.66%;
}
.products-grid.columns6 li:nth-child(5n+1) {
clear: none;
}
.products-grid.columns6 li:nth-child(6n+1) {
clear: left;
}
.products-grid.columns7 li {
width: 14.25%;
}
.products-grid.columns7 li:nth-child(5n+1) {
clear: none;
}
.products-grid.columns7 li:nth-child(7n+1) {
clear: left;
}
.products-grid.columns8 li {
width: 12.5%;
}
.products-grid.columns8 li:nth-child(5n+1) {
clear: none;
}
.products-grid.columns8 li:nth-child(8n+1) {
clear: left;
}
} }
small {
font-size: 85%; /*
font-weight: normal; * Product list
text-transform: lowercase; */
color: #999999 .products-list {
list-style: none;
padding: 0;
margin: 0 -10px 0;
} }
input:focus { .products-list li {
outline: none padding: 10px;
width: 100%;
} }
.page-title h1, .page-title h2 { .product {
color: #333333; position: relative;
font-size: 22px;
font-weight: 600;
text-transform: uppercase;
margin-top: 0px;
line-height: normal;
margin-bottom: 5px;
letter-spacing: 0.5px;
}
@media (max-width:767px) {
.page-title h1, .page-title h2 {
font-size: 26px
}
} }
/****************************************** .product .product-image-area {
9. product hover overflow: hidden;
******************************************/ position: relative;
.product-item { background-color: #fff;
list-style: none; border-radius: 0;
} }
.product-item .product-thumb { .product .product-image-area .product-image {
display: block;
overflow: hidden;
position: relative; position: relative;
border-radius: 0;
} }
.product-item .pr-info-area { .product .product-image-area .product-image img {
position: absolute; display: block;
bottom: 15px;
text-align: center;
width: 100%; width: 100%;
height: auto;
-webkit-transition: all 0.3s;
-moz-transition: all 0.3s;
transition: all 0.3s;
} }
.product-item .pr-info-area a { .product .product-image-area .product-image .product-hover-image {
margin: 0 8px;
}
.product-item .has-hover-img .hover-img {
position: absolute; position: absolute;
right: 0;
left: 0; left: 0;
top: 0; top: 0;
width: 100%;
transition: opacity 0.5s ease-in-out 0s;
-moz-transition: opacity 0.5s ease-in-out 0s;
-webkit-transition: opacity 0.5s ease-in-out 0s;
backface-visibility: hidden;
-moz-backface-visibility: hidden;
-webkit-backface-visibility: hidden;
opacity: 0; opacity: 0;
visibility: hidden;
} }
.product-item .has-hover-img:hover .hover-img { .product .product-image-area .product-actions {
opacity: 1; position: absolute;
} width: 100%;
.product-item .wishlist, .product-item .compare, .product-item .quick-view { bottom: 15px;
position: relative; left: 0;
text-align: center; text-align: left;
line-height: 35px; padding: 0 10px;
margin-bottom: 10px; visibility: hidden;
background-color: #fff;
-webkit-box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
opacity: 0; opacity: 0;
filter: alpha(opacity=0); -webkit-transition: opacity 0.2s;
color: #222; -moz-transition: opacity 0.2s;
display: inline-block; transition: opacity 0.2s;
transform: rotate(-45deg);
-webkit-transform: rotate(-45deg);
width: 35px;
height: 35px;
margin: 0 5px;
} }
.product-item:hover .wishlist, .product-item:hover .compare, .product-item:hover .quick-view { .product .product-image-area .product-actions .addtocart,
.product .product-image-area .product-actions .comparelink,
.product .product-image-area .product-actions .addtowishlist {
opacity: 1; opacity: 1;
transform: rotate(-45deg); visibility: visible;
-webkit-transform: rotate(-45deg); left: auto;
right: auto;
width: 32px;
height: 32px;
line-height: 30px;
font-size: 17px;
background-color: #fff;
-webkit-transition: all 0.2s;
-moz-transition: all 0.2s;
transition: all 0.2s;
text-align: center;
margin-top: 0;
margin-bottom: 0;
} }
.product-item .wishlist i, .product-item .compare i, .product-item .quick-view i { .product .product-quickview {
transform: rotate(45deg); position: absolute;
-webkit-transform: rotate(45deg); right: 0;
top: 0;
background-color: #000;
color: #fff;
padding: 10px;
z-index: 9;
border-radius: 0 0;
opacity: 0;
visibility: hidden;
-webkit-transition: all 0.1s;
-moz-transition: all 0.1s;
transition: all 0.1s;
font-size: 12px;
line-height: 1.4;
} }
.product-item .wishlist i span, .product-item .compare i span, .product-item .quick-view i span { .product .product-quickview span {
display: none; margin-left: 2px;
} }
.product-item .wishlist:hover, .product-item .compare:hover, .product-item .quick-view:hover { .product .product-quickview:hover, .product .product-quickview:focus {
opacity: 0.9 !important;
color: #fff; color: #fff;
background-color: #ffd401; text-decoration: none;
} }
.product-item .item-inner:hover:hover .animate1 { .product .product-quickview:hover span, .product .product-quickview:focus span {
-webkit-animation-name: flip; text-decoration: underline;
animation-name: flip;
} }
.product-item .item-inner:hover:hover .animate2 { .product .product-label {
-webkit-animation-name: bounce; position: absolute;
animation-name: bounce; right: 10px;
top: 10px;
color: #fff;
line-height: 1;
z-index: 5;
text-align: center;
} }
.product-item .item-inner:hover:hover .animate3 { .product .product-label + .product-label {
-webkit-animation-name: rubberBand; top: 40px;
animation-name: rubberBand;
} }
.product-item .item-inner:hover:hover .animate4 { .product .product-label span {
-webkit-animation-name: lightSpeedIn; display: block;
animation-name: lightSpeedIn; position: relative;
padding: 7px 10px;
font-size: 12px;
font-weight: 600;
text-transform: uppercase;
border-radius: 0;
} }
.product-item .item-inner:hover .animate5 { .product .product-label span.discount {
-webkit-animation-name: rotateIn; background-color: #e27c7c;
animation-name: rotateIn;
} }
.product-item .item-inner:hover .animate6 { .product .product-label span.new {
-webkit-animation-name: zoomInUp; background-color: #62b959;
animation-name: zoomInUp;
} }
.add-to-cart-mt { .product .product-details-area {
background: #f8f8f8; padding: 10px;
border: 0 none;
bottom: 0;
text-align: center; text-align: center;
text-transform: uppercase;
transition: all .5s ease-out 0s;
padding: 0px 15px 0px 0px;
color: #333333;
border: 1px #eee solid;
} }
.add-to-cart-mt i { .product .product-details-area .product-actions {
position: relative; margin: 0 -10px;
text-align: center;
line-height: 35px;
background-color: #ffd401;
filter: alpha(opacity=0);
color: #fff;
display: inline-block;
width: 35px;
height: 35px;
} }
.add-to-cart-mt:hover { .product .product-name {
background: #333333; color: #777;
color: #fff; font-weight: 400;
font-size: 14px;
margin: 0 0 3px;
} }
.add-to-cart-mt span { .product .product-name a {
margin-left: 10px color: #777;
} }
.pr-button .mt-button { .product .product-name a:hover, .product .product-name a:focus {
width: 33.3%; color: #000;
float: left; text-decoration: none;
text-align: center;
border-right: 1px solid
} }
.product-item .item-inner .item-info { .product:hover .product-image-area .product-actions {
text-align: center; visibility: visible;
padding-top: 12px; opacity: 1;
} }
.product-item .item-inner .item-info .pro-action { .product:hover .product-image-area .product-actions .addtocart {
margin-top: 10px; color: #000;
background-color: #fff;
border: 1px solid #000;
} }
.product-item .item-inner .item-info .item-title { .product:hover .product-image-area .product-actions .addtocart:hover, .product:hover .product-image-area .product-actions .addtocart:focus {
font-size: 12px; color: #fff;
margin-bottom: 6px; border-color: #000;
font-weight: 400; background-color: #000;
} }
@media (max-width:479px) { .product:hover .product-image-area .product-actions .comparelink:hover, .product:hover .product-image-area .product-actions .comparelink:focus {
.product-item .item-inner .item-info .item-title { background-color: #52b9b5;
font-size: 12px;
padding: 0px 10px
}
} }
.product-item .item-inner .item-info .item-title a { .product:hover .product-image-area .product-actions .addtowishlist:hover, .product:hover .product-image-area .product-actions .addtowishlist:focus {
display: inherit; background-color: #ed4949;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
padding: 0 10px;
font-size: 12px;
font-weight: 500;
text-transform: none;
} }
.product-item .item-inner { .product:hover .product-image-area .product-image .product-hover-image {
position: relative; visibility: visible;
overflow: hidden; background-color:#fff; padding-bottom:20px; opacity: 1;
} }
.product-item { .product:hover .product-quickview {
margin: 0 12px visibility: visible;
opacity: 1;
} }
.product-item .item-inner .item-info .item-title a:hover {
color: #ffd401; .product-price-box {
font-size: 0;
margin: 5px 0 3px;
} }
@media (max-width:479px) { .product-price-box .old-price,
.home-tab .product-item { .product-price-box .product-price {
margin: 0 8px display: inline-block;
} vertical-align: middle;
font-weight: 600;
} }
#best-sale-slider.product-flexslider { .product-price-box .old-price {
margin: 20px -12px color: #999;
font-size: 14px;
text-decoration: line-through;
} }
.best-sale-pro .slider-items-products .owl-buttons .owl-prev { .product-price-box .old-price + .product-price {
top: 15% margin-left: 6px;
} }
.best-sale-pro .slider-items-products .owl-buttons .owl-next { .product-price-box .product-price {
top: 15% font-size: 20px;
color: #444;
} }
/******************************************
21. Toolbar .product-actions a {
******************************************/
.pagination-area {
background: rgba(0,0,0,0) none repeat scroll 0 0;
border-top: 1px solid #eee;
margin: 10px 0 0;
padding-top: 20px;
text-align: center;
display: inline-block; display: inline-block;
width: 100% position: relative;
margin: 10px 1px;
vertical-align: middle;
border-radius: 0;
text-align: center;
-webkit-transition: all 0.3s;
-moz-transition: all 0.3s;
transition: all 0.3s;
} }
.pagination-area ul { .product-actions a:hover, .product-actions a:focus {
margin-bottom: 0px text-decoration: none;
} }
.pagination-area ul li { .product-actions a.addtocart {
background: #f5f5f5; color: #333;
color: #393939; background-color: #fff;
display: inline-block; font-size: 14px;
font-weight: 600; padding: 0 10px 0 8px;
height: 32px;
line-height: 30px; line-height: 30px;
margin: 0 0 0 5px; border: 1px solid #ccc;
border-radius: 4px
}
.pagination-area ul li a {
border: 1px solid #e0e0e0;
color: #363636;
display: block;
font-weight: 600;
text-decoration: none;
border-radius: 4px;
transition: all .4s ease-out .2s
} }
.pagination-area ul li a:hover { .product-actions a.addtocart i {
background: #333333; font-size: 15px;
border: 1px solid #333333; margin-right: 2px;
color: #fff;
} }
.pagination-area ul li a.active { .product:hover .product-actions a.addtocart {
background: #ffd401; background-color: #000;
border: 1px solid #ffd401; border-color: #000;
border-radius: 4px;
color: #fff; color: #fff;
transition: all .4s ease-out .2s
} }
/****************************************** .product-actions a.addtocart.outofstock {
26. products grid padding: 0 10px;
******************************************/ cursor: default;
.shop-inner { }
padding: 20px 0; .product:hover .product-actions a.addtocart.outofstock {
margin-bottom: 30px; color: #333 !important;
background-color: #fff !important;
border-color: #ccc !important;
}
.product-actions a.addtowishlist, .product-actions a.comparelink, .product-actions a.quickview {
font-size: 17px;
height: 32px;
width: 32px;
line-height: 32px;
background-color: transparent;
visibility: hidden;
opacity: 0;
} }
.products-grid { .hide-addtolinks .product-actions a.addtowishlist, .hide-addtolinks .product-actions a.comparelink, .hide-addtolinks .product-actions a.quickview {
margin: 0; display: none;
list-style: none
} }
ul.products-grid { .product-actions a.addtowishlist {
padding: 0; right: -37px;
margin: 0 color: #ed4949;
border: 1px solid #ed4949;
} }
.products-grid .item { .product-actions a.addtowishlist:hover {
margin-top: 30px; color: #fff;
padding: 0px background-color: #ed4949;
} }
.products-grid .product-item img { .product:hover .product-actions a.addtowishlist {
width: 100%; visibility: visible;
opacity: 1;
right: 0;
} }
@media (max-width:479px) { .product-actions a.comparelink {
.product-grid-area .products-grid .item { left: -37px;
width: 80%; color: #52b9b5;
float: none; border: 1px solid #52b9b5;
margin: 15px auto 15px
}
} }
.col-main .product-grid-area .products-grid { .product-actions a.comparelink:hover {
margin-left: -12px; color: #fff;
margin-right: -12px background-color: #52b9b5;
} }
/****************************************** .product:hover .product-actions a.comparelink {
27. products list visibility: visible;
******************************************/ opacity: 1;
.products-list h2 { left: 0;
font-size: 16px;
font-weight: bold;
text-align: left;
line-height: normal;
margin-bottom: 8px;
} }
ul.products-list { .product-actions a.quickview {
margin: auto; visibility: visible;
opacity: 1;
color: #000;
border: 1px solid #000;
} }
.products-list button.cart-button { .product-actions a.quickview:hover {
color: #fff; color: #fff;
background-color: #000;
} }
.products-list .item {
padding: 18px; .product.product-list:after {
overflow: hidden; content: '';
margin: 15px 0 30px !important; display: table;
list-style: none; background-color:#fff; clear: both;
} }
.products-list .rating { .product.product-list .product-details-area {
display: inline-block; text-align: left;
margin-right: 6px padding: 0 20px;
} }
.product-item .rating .fa.fa-star, .product.product-list .product-short-desc {
.jtv-category-area .jtv-product-content .fa.fa-star, font-size: 14px;
line-height: 1.5;
.products-list .rating .fa.fa-star,
.product-view-area .product-details-area .ratings .fa.fa-star { color:#ffd401;}
.product-item .rating .fa.fa-star-o,
.product-item .rating .fa.fa-star-half,
.jtv-category-area .jtv-product-content .fa.fa-star-o,
.jtv-category-area .jtv-product-content .fa.fa-star-half,
.products-list .rating .fa.fa-star-o,
.product-view-area .product-details-area .ratings .fa.fa-star-o{ color:#ccc;}
.products-list .rating-links {
display: inline-block;
margin-bottom: 4px
} }
.products-list .rating-links a { .product.product-list .product-ratings {
font-size: 11px; background: none;
margin-bottom: 10px;
} }
.products-list .item h3 { .product.product-list .product-name {
font-weight: normal; font-size: 18px;
color: #353535; margin: 10px 0;
padding-bottom: 2px
} }
.products-list .item h3 a { .product.product-list .product-price-box {
font-weight: normal; margin-top: 10px;
font-size: 12px;
color: #407CBF;
text-decoration: none
} }
.products-list .item h3 a:hover { .product.product-list .product-actions {
font-weight: normal; margin: 0;
font-size: 12px;
color: #333333;
text-decoration: underline
} }
.products-list .product-img { .product.product-list .product-actions a.addtowishlist, .product.product-list .product-actions a.comparelink, .product.product-list .product-actions a.quickview {
overflow: hidden; left: auto;
position: relative; right: auto;
padding-left: 0; visibility: visible;
width: 30%; opacity: 1;
float: left;
} }
.products-list .product-img img { .product.product-list .product-actions a.addtocart {
width: 100% background-color: #000;
border-color: #000;
color: #fff;
} }
.products-list .product-shop { @media (min-width: 600px) {
width: 67%; .product.product-list .product-image-area {
float: right; float: left;
width: 20%;
}
.product.product-list .product-details-area {
float: left;
width: 80%;
}
} }
.products-list .product-shop .desc {
font-size: 12px; .product-image-area img {
margin-bottom: 15px; width: 100%;
margin-top: 5px;
color: #666
} }
.products-list .product-shop p {
font-size: 12px; .product.product-sm {
color: #666; padding: 6px 0 8px;
line-height: 1.5em max-width: 300px;
}
.product.product-sm:after {
content: '';
display: table;
clear: both;
} }
.products-list .product-shop p.old-price { .product.product-sm .product-image-area {
margin-right: 4px width: 33%;
float: left;
padding: 2px;
} }
.products-list .product-shop .price-box { .product.product-sm .product-details-area {
margin: 5px 0 10px; float: left;
border-top: 1px solid #eee; width: 67%;
padding-top: 12px; text-align: left;
padding: 10px 16px;
} }
.products-list .desc a.link-learn { .product.product-sm .product-name {
font-size: 12px; font-size: 12px;
font-weight: bold;
margin-top: 5px;
color: #ffd401
} }
.products-list .product-shop button.button span { .product.product-sm .product-ratings {
padding: 1px 0 0 10px; background: none;
text-transform: uppercase margin-bottom: 5px;
} }
.products-list .product-shop .actions ul { .product.product-sm .product-price-box {
display: inline-block; margin: 2px 0 3px;
float: right;
margin-top: 8px
} }
.products-list .product-shop .actions ul li { .product.product-sm .product-price-box .old-price {
margin-right: 20px; font-size: 11px;
display: inline-block
} }
.products-list .product-shop .actions ul li a { .product.product-sm .product-price-box .old-price + .product-price {
text-transform: uppercase; margin-left: 3px;
font-size: 12px
} }
.products-list .product-shop .actions ul li:last-child a { .product.product-sm .product-price-box .product-price {
border-left: 1px solid #e5e5e5; font-size: 15px;
padding-left: 21px
} }
.products-list .product-shop .actions ul li:last-child {
margin-right: 0px /*
* Toolbar
*/
.toolbar-bottom {
text-align: center;
margin-top: 20px;
} }
.products-list .product-shop .actions ul li span { .toolbar-bottom .toolbar {
margin-left: 5px display: inline-block;
} }
/****************************************** .toolbar {
28. price box margin-bottom: 8px;
******************************************/
.price {
font-size: 12px;
color: #000;
white-space: nowrap !important;
} }
.price-box { .toolbar .sorter:after {
margin: 8px 0 2px content: '';
display: table;
clear: both;
}
.toolbar .sorter .pagination {
float: right;
margin: 0 0 5px 8px;
}
.toolbar .sorter .pagination a,
.toolbar .sorter .pagination span {
border-radius: 0;
min-width: 26px;
padding: 3px 6px;
margin-left: 5px;
text-align: center;
} }
.regular-price {
display: inline /*
* Form style
*/
.form-section {
overflow: hidden;
} }
.regular-price .price { .form-section .featured-box {
font-weight: bold; margin-bottom: 30px;
font-size: 16px;
color: #000
} }
.block .regular-price, .block .regular-price .price { .form-section .featured-box .box-content {
color: #333333 padding: 35px 25px;
} }
.price-box .price-from .price { @media (min-width: 768px) {
font-weight: 600; .form-section .form-content {
font-size: 12px; min-height: 275px;
color: #333333 }
} }
.price-box .price-to .price { .form-section p {
font-weight: 600; margin-bottom: 15px;
font-size: 12px;
color: #333333
} }
.price-box .minimal-price .price { .form-section .form-action {
font-weight: 600; padding-top: 8px;
font-size: 12px; margin-top: 1em;
color: #333333 text-align: right;
} }
.old-price .price-label { .form-section .form-action a:not(.btn) {
white-space: nowrap; color: #000;
color: #999;
display: none
} }
.old-price { .form-section .form-action a:not(.btn).pull-left {
display: inline; line-height: 38px;
margin-right: 15px
} }
.old-price .price-label { .form-section .form-action a:not(.btn):hover, .form-section .form-action a:not(.btn):focus {
color: #777777; color: #050505;
display: none; text-decoration: underline;
white-space: nowrap
} }
.old-price .price { .form-section .required {
color: #777777!important; color: #eb340a;
font-size: 14px; font-size: 11px;
font-weight: normal; text-align: right;
text-decoration: line-through;
margin-left: 6px
} }
.special-price { @media (min-width: 992px) {
margin: 0; .form-section.register-form .row {
padding: 3px 0; margin-left: -30px;
display: inline margin-right: -30px;
}
.form-section.register-form [class*="col-"] {
padding-left: 30px;
padding-right: 30px;
}
} }
.special-price .price-label { .form-control {
color: #666; border-radius: 0;
display: none; height: 38px;
font-size: 12px; padding-top: 8px;
font-weight: 400; padding-bottom: 8px;
white-space: nowrap
} }
.special-price .price { .form-group .field_with_errors {
font-size: 16px; display: inline;
color: #000;
font-weight: bold
} }
.special-price .price-label {
font-size: 12px; /*
font-weight: 600; * Feature Box
white-space: nowrap; */
color: #666; .featured-box {
display: none background: #F5F5F5;
background: -webkit-linear-gradient( top , #FFF 1%, #fcfcfc 98%) repeat scroll 0 0 transparent;
background: linear-gradient(to bottom, #FFF 1%, #fcfcfc 98%) repeat scroll 0 0 transparent;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
border-bottom: 1px solid #DFDFDF;
border-left: 1px solid #ECECEC;
border-radius: 8px;
border-right: 1px solid #ECECEC;
box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.04);
margin: 20px auto;
min-height: 100px;
position: relative;
text-align: center;
z-index: 1;
} }
.minimal-price {
margin: 0 html .featured-box-primary .box-content {
border-top-color: #000000;
} }
.minimal-price .price-label {
white-space: nowrap .featured-box {
box-shadow: 0 2px 3px rgba(0, 0, 0, 0.08);
} }
.minimal-price-link { .featured-box .box-content {
display: inline border-radius: 8px;
border-top: 4px solid #CCC;
padding: 30px 30px 10px 30px;
position: relative;
top: -1px;
} }
/* Featured Box Left */
.featured-box-text-left {
text-align: left;
}
\ No newline at end of file
body {
font-size: 13px;
line-height: 1.5;
background: image-url('shop-14-bg.png') top center repeat-x;
}
a {
color: #000000;
}
a:hover {
color: #0d0d0d;
}
a:focus {
color: #0d0d0d;
}
a:active {
color: #000000;
}
.pagination > li > a,
.pagination > li > span,
.pagination > li > a:hover,
.pagination > li > span:hover,
.pagination > li > a:focus,
.pagination > li > span:focus {
color: #000000;
}
.pagination > .active > a,
.pagination > .active > span,
.pagination > .active > a:hover,
.pagination > .active > span:hover,
.pagination > .active > a:focus,
.pagination > .active > span:focus {
background-color: #000000 !important;
border-color: #000000;
}
html .btn-primary {
color: #ffffff;
background-color: #000000;
border-color: #000000 #000000 #000000;
}
html .btn-primary:hover {
border-color: #0d0d0d #0d0d0d #000000;
background-color: #0d0d0d;
}
html .btn-primary:active,
html .btn-primary:focus,
html .btn-primary:active:hover,
html .btn-primary:active:focus {
border-color: #000000 #000000 #000000;
background-color: #000000;
}
html .btn-primary.dropdown-toggle {
border-left-color: #000000;
}
html .btn-primary[disabled],
html .btn-primary[disabled]:hover,
html .btn-primary[disabled]:active,
html .btn-primary[disabled]:focus {
border-color: #333333;
background-color: #333333;
}
html .btn-primary:hover,
html .btn-primary:focus,
html .btn-primary:active:hover,
html .btn-primary:active:focus {
color: #ffffff;
}
.btn {
border-radius: 0;
}
.featured-box .box-content {
border-radius: 0;
}
.product-essential {
margin-bottom: 50px;
}
@media (max-width: 767px) {
.product-img-box {
margin-bottom: 25px;
}
}
.product-img-box img {
display: block;
width: 100%;
height: auto;
}
.product-img-box .product-img-wrapper {
padding: 3px;
border: 1px solid #ddd;
border-radius: 0;
}
.product-img-box-wrapper {
position: relative;
margin-bottom: 10px;
}
.product-details-box .product-name {
margin: 15px 0;
font-size: 28px;
font-weight: 600;
line-height: 1;
color: #555;
}
.product-details-box .product-short-desc {
padding: 0 0 10px;
border-bottom: 1px solid #ebebeb;
}
.product-details-box .product-short-desc p {
font-size: 14px;
line-height: 1.65;
margin: 0 0 20px;
}
.product-details-box .product-detail-info {
padding-bottom: 20px;
margin-top: 20px;
border-bottom: 1px solid #ebebeb;
}
.product-details-box .product-detail-info .product-price-box {
margin: 0 0 20px;
}
.product-details-box .product-detail-info .product-price-box .product-price {
font-size: 33px;
line-height: 1;
color: #000;
}
#header .header-body {
border-top: none;
background-color: transparent;
border-bottom: none;
padding: 0;
}
@media (min-width: 992px) {
#header .header-body {
padding-bottom: 15px;
}
}
#header .header-logo img {
margin: 0 24px 0 0;
}
#header .header-container {
padding-top: 28px;
padding-bottom: 28px;
}
#header .header-container.header-nav {
padding: 0;
}
#header .cart-area {
float: right;
vertical-align: middle;
}
@media (max-width: 991px) {
#header .cart-area {
margin-top: 5.5px;
}
}
header .header-search {
position: relative;
float: right;
margin: 0;
font-size: 0;
line-height: 1;
padding: 0;
border: none;
}
@media (min-width: 992px) {
#header .header-search {
float: left;
}
}
@media (max-width: 991px) {
#header .header-search {
margin-top: 5.5px;
margin-left: 5px;
margin-right: 5px;
}
}
@media (max-width: 360px) {
#header .header-search {
margin-left: 0;
margin-right: 0;
}
}
#header .header-search .search-toggle {
display: inline-block;
font-size: 14px;
line-height: 40px;
min-width: 25px;
text-align: center;
}
@media (min-width: 992px) {
#header .header-search .search-toggle {
display: none;
}
}
#header .header-search form {
display: inline-block;
width: 0;
}
@media (min-width: 992px) {
#header .header-search form {
width: 400px;
}
}
@media (min-width: 992px) {
#header .header-search form {
width: 450px;
}
}
#header .header-search .header-search-wrapper {
display: none;
overflow: visible;
border: 1px solid #ccc;
border-radius: 0;
position: relative;
width: 100%;
min-width: 250px;
padding-right: 170px;
background-color: #fff;
}
#header .header-search .header-search-wrapper.open {
display: block;
}
#header .header-search .header-search-wrapper:after {
content: '';
display: table;
clear: both;
}
@media (min-width: 992px) {
#header .header-search .header-search-wrapper {
display: block;
}
}
@media (max-width: 991px) {
#header .header-search .header-search-wrapper {
position: absolute;
right: -50px;
top: 100%;
border-width: 5px;
width: 450px;
border-radius: 0;
}
#header .header-search .header-search-wrapper:before {
content: "";
display: block;
position: absolute;
right: 45px;
top: -25px;
width: 20px;
height: 20px;
border: 10px solid transparent;
border-bottom-color: #ccc;
}
}
@media (max-width: 480px) {
#header .header-search .header-search-wrapper {
width: 300px;
}
}
@media (max-width: 350px) {
#header .header-search .header-search-wrapper {
width: 240px;
}
}
#header .header-search .header-search-wrapper .form-control,
#header .header-search .header-search-wrapper select {
float: left;
height: 38px;
font-family: Arial;
font-size: 13px;
background-color: #fff;
margin: 0;
}
#header .header-search .header-search-wrapper .form-control {
padding: 9px 15px;
color: #999;
width: 100%;
margin: 0;
line-height: 20px;
border-radius: 0 0 0 0;
box-shadow: none;
border: none;
}
#header .header-search .header-search-wrapper select {
position: absolute;
right: 40px;
width: 130px;
border: 1px solid #ccc;
border-top: 0;
border-bottom: 0;
line-height: 36px;
color: #777;
padding: 2px 0;
padding-left: 10px;
border-radius: 0;
-moz-appearance: none;
-webkit-appearance: none;
}
@media (max-width: 350px) {
#header .header-search .header-search-wrapper select {
width: 110px;
}
}
#header .header-search .header-search-wrapper .btn.btn-default {
position: absolute;
left: auto;
right: 0;
top: 0;
width: 40px;
height: 38px;
color: #777;
background-color: transparent;
font-size: 14px;
border: 0;
padding: 0;
margin: 0;
background: transparent;
cursor: pointer;
border-radius: 0 0 0 0;
}
#header .header-search .header-search-wrapper .btn.btn-default:hover, #header .header-search .header-search-wrapper .btn.btn-default:focus {
color: #000;
background-color: transparent;
}
.panel-default>.panel-heading {
color: #333;
background-color: #f5f5f5;
border-color: #ddd;
}
.sidebar.shop-sidebar .panel-group {
margin-bottom: 40px;
}
.sidebar.shop-sidebar .panel-group .panel + .panel {
margin-top: 14px;
}
.sidebar.shop-sidebar .panel.panel-default {
border-radius: 0;
border: none;
overflow: hidden;
}
.sidebar.shop-sidebar .panel.panel-default .panel-heading {
border-radius: 0;
}
.sidebar.shop-sidebar .panel.panel-default .panel-heading .panel-title {
margin: 0;
font-size: 13px;
font-weight: 700;
text-transform: uppercase;
color: #777;
}
.sidebar.shop-sidebar .panel.panel-default .panel-heading a {
position: relative;
border-radius: 0 0 0 0;
padding-top: 11.5px;
padding-bottom: 11.5px;
padding-right: 45px;
color: #777;
border: 1px solid #ddd;
}
.sidebar.shop-sidebar .panel.panel-default .panel-heading a:before {
font-family: 'FontAwesome';
content: "\f0d8";
width: 26px;
height: 26px;
display: block;
border: 1px solid #ddd;
position: absolute;
right: 15px;
top: 50%;
margin-top: -13px;
border-radius: 0;
color: #ccc;
text-align: center;
line-height: 24px;
background-color: #fff;
font-size: 10px;
}
.sidebar.shop-sidebar .panel.panel-default .panel-heading a:hover:before {
background-color: #000;
border-color: #000;
color: #fff;
}
.sidebar.shop-sidebar .panel.panel-default .panel-heading a.collapsed {
border-radius: 0;
}
.sidebar.shop-sidebar .panel.panel-default .panel-heading a.collapsed:before {
content: "\f0d7";
}
.sidebar.shop-sidebar .panel.panel-default .panel-body {
padding: 15px 15px 7px;
background-color: #fbfbfb;
}
#footer {
border-top: none;
background-color: #54555e;
color: #bbbbbb;
font-size: 13px;
padding-top: 0;
}
#footer .footer-copyright {
color: #bbb;
background-color: #45464e;
border-top: none;
padding: 29.5px 0;
margin-top: 0;
}
@media (min-width: 992px) {
#footer .footer-copyright .logo,
#footer .footer-copyright .social-icons,
#footer .footer-copyright .footer-payment {
float: left;
margin-bottom: 0;
}
#footer .footer-copyright .logo {
margin-right: 45px;
}
#footer .footer-copyright .social-icons {
margin-right: 60px;
}
#footer .footer-copyright .social-icons li {
margin-bottom: 0;
}
#footer .footer-copyright .footer-payment {
margin-top: 1px;
}
#footer .footer-copyright .copyright-text {
float: right;
margin-bottom: 0;
margin-top: 6px;
}
}
@media (max-width: 991px) {
#footer .footer-copyright {
text-align: center;
}
#footer .footer-copyright .logo {
margin-bottom: 8px;
}
#footer .footer-copyright .social-icons {
margin-bottom: 5px;
}
#footer .footer-copyright .logo img,
#footer .footer-copyright .footer-payment {
margin-left: auto;
margin-right: auto;
}
#footer .footer-copyright .footer-payment {
margin-bottom: 10px;
}
}
\ No newline at end of file
class ApplicationController < ActionController::Base class ApplicationController < ActionController::Base
protect_from_forgery with: :exception protect_from_forgery with: :exception
# rescue_from ActiveRecord::RecordNotFound, :with => :render_404
def render_404
respond_to do |format|
format.html { render file: "#{Rails.root}/public/404", layout: false, status: :not_found }
format.xml { head :not_found }
format.any { head :not_found }
end
end
end end
class ProductsController < ApplicationController class ProductsController < ApplicationController
before_action :authenticate_user!, only: [:new, :edit, :create, :update, :destroy]
before_action :set_product, only: :show
before_action :user_can_edit_product, only: [:edit, :update, :destroy]
# GET /products/new
def new
@product = Product.new
end
# POST /products
def create
@product = Product.new(product_params.merge(user_id: current_user.id))
if @product.save
redirect_to root_url, flash: { success: "Product #{@product.title} is sucessfully created" }
else
render 'new'
end
end
# PATCH/PUT /products/1
def update
if @product.update(product_params)
redirect_to root_url, flash: { success: "Product #{@product.title} is sucessfully updated" }
else
render 'edit'
end
end
# DELETE /products/1
def destroy
if @product.destroy
flash[:success] = "Product #{@product.title} deleted"
else
flash[:alert] = "Product #{@product.title} can't be deleted"
end
redirect_to root_url
end
private
# Use callbacks to share common setup or constraints between actions.
def set_product
@product = Product.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def product_params
params.require(:product).permit(:title, :sku, :price, :description,
:category_id, :image_url)
end
# Is current user own current editing product?
def user_can_edit_product
@product = current_user.products.find_by(id: params[:id])
redirect_to root_url, flash: { alert: 'You do not have permission to edit this product' } if @product.blank?
end
end end
\ No newline at end of file
module ProductsHelper module ProductsHelper
def get_product_thumbnail(product, thumbnail_width, thumbnail_height) def get_product_thumbnail(product, thumbnail_width, thumbnail_height)
product.image_url || default_img_path = "product/placeholder_#{thumbnail_width}x#{thumbnail_height}"
"product/placeholder_#{thumbnail_width}x#{thumbnail_height}" # product.image_url always returns PictureUploader object
product.image_url? ? product.image_url : default_img_path
end end
end end
\ No newline at end of file
class Product < ApplicationRecord class Product < ApplicationRecord
belongs_to :category belongs_to :category
end belongs_to :user
\ No newline at end of file
mount_uploader :image_url, PictureUploader
validates :category_id, presence: true
validates :user_id, presence: true
end
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :products, dependent: :destroy
end
class PictureUploader < CarrierWave::Uploader::Base
# Include RMagick or MiniMagick support:
# include CarrierWave::RMagick
# include CarrierWave::MiniMagick
# Choose what kind of storage to use for this uploader:
storage :file
# storage :fog
# Override the directory where uploaded files will be stored.
# This is a sensible default for uploaders that are meant to be mounted:
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
# Provide a default URL as a default if there hasn't been a file uploaded:
# def default_url(*args)
# # For Rails 3.1+ asset pipeline compatibility:
# # ActionController::Base.helpers.asset_path("fallback/" + [version_name, "default.png"].compact.join('_'))
#
# "/images/fallback/" + [version_name, "default.png"].compact.join('_')
# end
# Process files as they are uploaded:
# process scale: [200, 300]
#
# def scale(width, height)
# # do something
# end
# Create different versions of your uploaded files:
# version :thumb do
# process resize_to_fit: [50, 50]
# end
# Add a white list of extensions which are allowed to be uploaded.
# For images you might use something like this:
# def extension_whitelist
# %w(jpg jpeg gif png)
# end
# Override the filename of the uploaded files:
# Avoid using model.id or version_name here, see uploader/store.rb for details.
# def filename
# "something.jpg" if original_filename
# end
end
<div class="container">
<div class="row">
<div class="col-md-9 col-md-push-3 my-account form-section">
<h1 class="h2 heading-primary font-weight-normal">
Edit <%= resource_name.to_s.humanize %>
</h1>
<div class="featured-box featured-box-primary featured-box-flat featured-box-text-left mt-md">
<div class="box-content">
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
<h4 class="heading-primary text-uppercase mb-lg">
ACCOUNT INFORMATION
</h4>
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<%= f.label :email %>
<%= f.email_field :email, autofocus: true, class: "form-control" %>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<%= f.label :password %>
<% if @minimum_password_length %>
<em>(<%= @minimum_password_length %> characters minimum)</em>
<% end %>
<%= f.password_field :password, autocomplete: "off", class: "form-control" %>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation, autocomplete: "off", class: "form-control" %>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br />
<%= f.password_field :current_password, autocomplete: "off", class: "form-control" %>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="form-action clearfix mt-none">
<%= f.submit "Submit", class: "btn btn-primary" %>
</div>
</div>
</div>
<% end %>
</div>
</div>
</div>
</div>
</div>
<section class="form-section register-form">
<div class="container">
<h1 class="h2 heading-primary font-weight-normal mb-md mt-xlg">
Create an Account
</h1>
<div class="featured-box featured-box-primary featured-box-flat featured-box-text-left mt-md">
<div class="box-content">
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<h4 class="heading-primary text-uppercase mb-lg">PERSONAL INFORMATION</h4>
<%= devise_error_messages! %>
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<%= f.label :email %>
<%= f.email_field :email, autofocus: true, class: "form-control" %>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<%= f.label :password %>
<% if @minimum_password_length %>
<em>(<%= @minimum_password_length %> characters minimum)</em>
<% end %>
<%= f.password_field :password, autocomplete: "off", class: "form-control" %>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation, autocomplete: "off", class: "form-control" %>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="form-action clearfix mt-none">
<%= f.submit "Submit", class: "btn btn-primary" %>
</div>
</div>
</div>
<% end %>
</div>
</div>
</div>
</section>
<section class="form-section">
<div class="container">
<h2 class="h2 heading-primary font-weight-normal mb-md mt-xlg">
Welcome back! Sign in to your account
</h2>
<div class="featured-box featured-box-primary featured-box-flat featured-box-text-left mt-md">
<div class="box-content">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
<div class="form-content">
<h3 class="heading-text-color font-weight-normal">
</h3>
<div class="form-group">
<%= f.label :email %>
<%= f.email_field :email, autofocus: true, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :password %>
<%= f.password_field :password, autocomplete: "off", class: "form-control" %>
</div>
</div>
<div class="form-action clearfix">
<%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %>
<%= link_to "Lost your password?", new_password_path(resource_name), class: 'pull-left' %>
<% end -%>
<%= f.submit "Submit", class: "btn btn-primary" %>
</div>
<% end %>
</div>
</div>
</div>
</div>
</div>
</section>
\ No newline at end of file
<%- if controller_name != 'sessions' %>
<%= link_to "Log in", new_session_path(resource_name) %><br />
<% end -%>
<%- if devise_mapping.registerable? && controller_name != 'registrations' %>
<%= link_to "Sign up", new_registration_path(resource_name) %><br />
<% end -%>
<%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %>
<%= link_to "Forgot your password?", new_password_path(resource_name) %><br />
<% 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 -%>
<footer id="footer">
<div class="footer-copyright">
<div class="container">
<a href="" class="logo">
<%= image_tag('logo-footer.png', class: 'img-responsive') %>
</a>
<p class="copyright-text">
© Copyright 2017. All Rights Reserved.
</p>
</div>
</div>
</footer>
\ No newline at end of file
<header id="header">
<div class="header-body">
<div class="header-top">
<div class="container">
</div>
</div>
<div class="header-container container">
<div class="header-row">
<div class="header-column">
<div class="header-logo">
<a href="<%= root_url %>">
<%= image_tag 'logo-header.png' %>
</a>
</div>
</div>
<div class="header-column">
<div class="header-row">
<div class="cart-area">
</div>
<div class="header-search">
</div>
</div>
</div>
</div>
</div>
<div class="header-container header-nav">
</div>
</div>
</header>
\ No newline at end of file
...@@ -9,12 +9,10 @@ ...@@ -9,12 +9,10 @@
</head> </head>
<body> <body>
<div id="main-container col2-left-layout"> <%= render 'layouts/header' %>
<div class="container"> <div role="main" class="main">
<%= yield %> <%= yield %>
</div>
<!-- End .container -->
</div> </div>
<!-- End .main-container --> <%= render 'layouts/footer' %>
</body> </body>
</html> </html>
<%= form_for(@product) do |f| %>
<%= render 'shared/error_messages', object: @product %>
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<%= f.label :title %>
<%= f.text_field :title, class: "form-control" %>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<%= f.label :sku %>
<%= f.text_field :sku, class: "form-control", step: :any %>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<%= f.label :price %>
<%= f.number_field :price, class: "form-control" %>
</div>
</div>
<div class="col-xs-12">
<div class="form-group">
<%= f.label :description %>
<%= f.text_area :description, class: "form-control", rows: 7 %>
</div>
</div>
<div class="col-xs-12">
<div class="form-group">
<%= f.label :category, 'Product Category' %>
<%= f.collection_select :category_id, Category.all, :id, :title, { prompt: 'Select a category' }, class: 'form-control' %>
</div>
</div>
<div class="col-xs-12">
<div class="form-group">
<%= f.label :image_url, 'Product Image' %>
<%= f.file_field :image_url %>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="form-action clearfix mt-none">
<%= f.submit "Submit", class: "btn btn-primary" %>
</div>
</div>
</div>
</div>
<% end %>
\ No newline at end of file
<li class="item"> <li class="product-<%= product.id %>">
<div class="product-img"> <div class="product product-list">
<a href="#"> <figure class="product-image-area">
<figure> <%= link_to image_tag(get_product_thumbnail(product, 170, 204)), product_path(product) %>
<%= image_tag(get_product_thumbnail(product, 170, 204), class: "small-image") %> </figure>
</figure> <div class="product-details-area">
</a> <h2 class="product-name">
</div> <%= link_to product.title, product_url(product) %>
<div class="product-shop"> </h2>
<h2 class="product-name"><%= product.title %></h2> <div class="product-short-desc">
<div class="price-box"> <%= product.description %>
<div class="special-price"> </div>
<span class="price"><%= number_to_currency(product.price) %></span> <div class="product-price-box">
<span class="product-price"><%= number_to_currency(product.price) %></span>
</div> </div>
</div>
<div class="desc std">
<%= simple_format(product.description) %>
</div> </div>
</div> </div>
</li> </li>
\ No newline at end of file
<ul class="products-grid">
<% @recommended_products.each do |product| %> <% @recommended_products.each do |product| %>
<li class="item col-lg-4 col-md-4 col-sm-6 col-xs-6 "> <li class="product-<%= product.id %>">
<div class="product-item"> <div class="product">
<div class="item-inner"> <figure class="product-image-area">
<div class="product-thumb"> <%= link_to image_tag(get_product_thumbnail(product, 170, 204)), product_path(product) %>
<figure>
<a href="#">
<%= image_tag(get_product_thumbnail(product, 170, 204)) %>
</a>
</figure> </figure>
</div> <div class="product-details-area">
<div class="item-info"> <h2 class="product-name">
<div class="info-inner"> <%= link_to product.title, product_url(product) %>
<div class="item-title"> </h2>
<%= link_to product.title, "#" %> <div class="product-price-box">
</div> <span class="product-price"><%= number_to_currency(product.price) %></span>
<div class="item-content">
<div class="item-price">
<div class="price-box">
<div class="regular-price">
<span class="price"><%= number_to_currency(product.price) %></span>
</div>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </li>
</div> <% end %>
</li> \ No newline at end of file
<% end %>
</ul>
\ No newline at end of file
<div class="container">
<div class="row">
<div class="col-md-9 col-md-push-3 create-product form-section">
<h1 class="h2 heading-primary font-weight-normal">
Edit Product #<%= @product.id %>
(<%= link_to 'Delete', @product, method: :delete, data: { confirm: 'Are your sure?' } %>)
</h1>
<div class="featured-box featured-box-primary featured-box-flat featured-box-text-left mt-md">
<div class="box-content">
<%= render 'form' %>
</div>
</div>
</div>
</div>
</div>
\ No newline at end of file
<div class="container">
<div class="row">
<div class="col-md-9 col-md-push-3 create-product form-section">
<h1 class="h2 heading-primary font-weight-normal">
New Product
</h1>
<div class="featured-box featured-box-primary featured-box-flat featured-box-text-left mt-md">
<div class="box-content">
<%= render 'form' %>
</div>
</div>
</div>
</div>
</div>
\ No newline at end of file
<div class="container">
<div class="product-view">
<div class="product-essential">
<div class="row">
<div class="product-img-box col-sm-5">
<div class="product-img-box-wrapper">
<div class="product-img-wrapper">
<%= image_tag(get_product_thumbnail(@product, 715, 952)) %>
</div>
</div>
</div>
<div class="product-details-box col-sm-7">
<h1 class="product-name">
<%= @product.title %>
</h1>
<div class="product-short-desc">
<%= @product.description %>
</div>
<div class="product-detail-info">
<div class="product-price-box">
<span class="product-price"><%= number_to_currency(@product.price) %></span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
\ No newline at end of file
<% if object.errors.any? %>
<div id="error_explanation" class="alert alert-danger">
<h4><%= pluralize(object.errors.count, "error") %> prohibited your data from being saved:</h4>
<ul>
<% object.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
\ No newline at end of file
<% flash.each do |message_type, message| %>
<div class="alert alert-<%= message_type %>">
<span><%= message %></span>
</div>
<% end %>
\ No newline at end of file
<div class="panel-group">
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
Categories
</h4>
</div>
<div id="panel-filter-category">
<div class="panel-body">
<ul>
</ul>
</div>
</div>
</div>
</div>
\ No newline at end of file
<div class="row"> <div class="container">
<div class="col-main col-sm-9 col-xs-12 col-sm-push-3"> <div class="row">
<div class="shop-inner"> <div class="col-md-9 col-md-push-3">
<div class="page-title"> <%= render 'shared/flash_messages' %>
<h1>Recommended Items</h1> <h2 class="h2 heading-primary mt-lg clearfix">
</div> <span>Recommended Items</span>
<div class="product-grid-area"> </h2>
<div class="products-grid columns3">
<%= render 'products/recommended' %> <%= render 'products/recommended' %>
</div> </div>
<div class="page-title"> <h2 class="h2 heading-primary mt-lg clearfix">
<h1>Newest Items</h1> <span>Newest Items</span>
</div> </h2>
<div class="product-list-area"> <ul class="products-list">
<ul id="products-list" class="products-list"> <%= render @latest_products %>
<%= render @latest_products %> </ul>
</ul>
</div>
<div class="pagination-area"> <div class="toolbar-bottom">
<%= paginate @latest_products %> <div class="toolbar">
<div class="sorter">
<%= paginate @latest_products, theme: 'bootstrap' %>
</div>
</div>
</div> </div>
</div> </div>
<div class="col-md-3 col-md-pull-9 sidebar shop-sidebar">
<%= render 'shared/sidebar' %>
</div>
</div> </div>
<aside class="sidebar col-sm-3 col-xs-12 col-sm-pull-9">
<%= render 'shared/sidebar' %>
</aside>
</div> </div>
# MySQL. Versions 5.1.10 and up are supported.
#
# Install the MySQL driver
# gem install mysql2
#
# Ensure the MySQL gem is defined in your Gemfile
# gem 'mysql2'
#
# And be sure to use new-style password hashing:
# http://dev.mysql.com/doc/refman/5.7/en/old-client.html
#
default: &default
adapter: mysql2
encoding: utf8
username: root
password: 123123
development:
<<: *default
database: dhp_venshop_development
# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
<<: *default
database: dhp_venshop_test
# As with config/secrets.yml, you never want to store sensitive information,
# like your database password, in your source code. If your source code is
# ever seen by anyone, they now have access to your database.
#
# Instead, provide the password as a unix environment variable when you boot
# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
# for a full rundown on how to provide these environment variables in a
# production deployment.
#
# On Heroku and other platform providers, you may have a full connection URL
# available as an environment variable. For example:
#
# DATABASE_URL="mysql2://myuser:mypass@localhost/somedatabase"
#
# You can use this database configuration with:
#
# production:
# url: <%= ENV['DATABASE_URL'] %>
#
production:
<<: *default
database: dhp_venshop_production
username: dhp_venshop
password: <%= ENV['DHP_VENSHOP_DATABASE_PASSWORD'] %>
# Use this hook to configure devise mailer, warden hooks and so forth.
# Many of these configuration options can be set straight in your model.
Devise.setup do |config|
# The secret key used by Devise. Devise uses this key to generate
# random tokens. Changing this key will render invalid all existing
# confirmation, reset password and unlock tokens in the database.
# Devise will use the `secret_key_base` as its `secret_key`
# by default. You can change it below and use your own secret key.
# config.secret_key = 'f196dd770a03b428e78ff2512c0f8212315406501c10006d2c279c30af5e889641a1f675b19dc8cc8239fe75185cc40f9744f2bee98aac1145962758c23dc7c9'
# ==> Mailer Configuration
# Configure the e-mail address which will be shown in Devise::Mailer,
# note that it will be overwritten if you use your own mailer class
# with default "from" parameter.
config.mailer_sender = 'please-change-me-at-config-initializers-devise@example.com'
# Configure the class responsible to send e-mails.
# config.mailer = 'Devise::Mailer'
# Configure the parent class responsible to send e-mails.
# config.parent_mailer = 'ActionMailer::Base'
# ==> ORM configuration
# Load and configure the ORM. Supports :active_record (default) and
# :mongoid (bson_ext recommended) by default. Other ORMs may be
# available as additional gems.
require 'devise/orm/active_record'
# ==> Configuration for any authentication mechanism
# Configure which keys are used when authenticating a user. The default is
# just :email. You can configure it to use [:username, :subdomain], so for
# authenticating a user, both parameters are required. Remember that those
# parameters are used only when authenticating and not when retrieving from
# session. If you need permissions, you should implement that in a before filter.
# You can also supply a hash where the value is a boolean determining whether
# or not authentication should be aborted when the value is not present.
# config.authentication_keys = [:email]
# Configure parameters from the request object used for authentication. Each entry
# given should be a request method and it will automatically be passed to the
# find_for_authentication method and considered in your model lookup. For instance,
# if you set :request_keys to [:subdomain], :subdomain will be used on authentication.
# The same considerations mentioned for authentication_keys also apply to request_keys.
# config.request_keys = []
# Configure which authentication keys should be case-insensitive.
# These keys will be downcased upon creating or modifying a user and when used
# to authenticate or find a user. Default is :email.
config.case_insensitive_keys = [:email]
# Configure which authentication keys should have whitespace stripped.
# These keys will have whitespace before and after removed upon creating or
# modifying a user and when used to authenticate or find a user. Default is :email.
config.strip_whitespace_keys = [:email]
# Tell if authentication through request.params is enabled. True by default.
# It can be set to an array that will enable params authentication only for the
# given strategies, for example, `config.params_authenticatable = [:database]` will
# enable it only for database (email + password) authentication.
# config.params_authenticatable = true
# Tell if authentication through HTTP Auth is enabled. False by default.
# It can be set to an array that will enable http authentication only for the
# given strategies, for example, `config.http_authenticatable = [:database]` will
# enable it only for database authentication. The supported strategies are:
# :database = Support basic authentication with authentication key + password
# config.http_authenticatable = false
# If 401 status code should be returned for AJAX requests. True by default.
# config.http_authenticatable_on_xhr = true
# The realm used in Http Basic Authentication. 'Application' by default.
# config.http_authentication_realm = 'Application'
# It will change confirmation, password recovery and other workflows
# to behave the same regardless if the e-mail provided was right or wrong.
# Does not affect registerable.
# config.paranoid = true
# By default Devise will store the user in session. You can skip storage for
# particular strategies by setting this option.
# Notice that if you are skipping storage for all authentication paths, you
# may want to disable generating routes to Devise's sessions controller by
# passing skip: :sessions to `devise_for` in your config/routes.rb
config.skip_session_storage = [:http_auth]
# By default, Devise cleans up the CSRF token on authentication to
# avoid CSRF token fixation attacks. This means that, when using AJAX
# requests for sign in and sign up, you need to get a new CSRF token
# from the server. You can disable this option at your own risk.
# config.clean_up_csrf_token_on_authentication = true
# When false, Devise will not attempt to reload routes on eager load.
# This can reduce the time taken to boot the app but if your application
# requires the Devise mappings to be loaded during boot time the application
# won't boot properly.
# config.reload_routes = true
# ==> Configuration for :database_authenticatable
# For bcrypt, this is the cost for hashing the password and defaults to 11. If
# using other algorithms, it sets how many times you want the password to be hashed.
#
# Limiting the stretches to just one in testing will increase the performance of
# your test suite dramatically. However, it is STRONGLY RECOMMENDED to not use
# a value less than 10 in other environments. Note that, for bcrypt (the default
# algorithm), the cost increases exponentially with the number of stretches (e.g.
# a value of 20 is already extremely slow: approx. 60 seconds for 1 calculation).
config.stretches = Rails.env.test? ? 1 : 11
# Set up a pepper to generate the hashed password.
# config.pepper = '3274e2320fa3bb73652bbec37129608f37f592907ece3a8736bf58b501e44ffb5cd0a0bf64150ce9735093e7fe7f93826e728985b2e23e37ceb57f537c1a85b5'
# Send a notification to the original email when the user's email is changed.
# config.send_email_changed_notification = false
# Send a notification email when the user's password is changed.
# config.send_password_change_notification = false
# ==> Configuration for :confirmable
# A period that the user is allowed to access the website even without
# confirming their account. For instance, if set to 2.days, the user will be
# able to access the website for two days without confirming their account,
# access will be blocked just in the third day. Default is 0.days, meaning
# the user cannot access the website without confirming their account.
# config.allow_unconfirmed_access_for = 2.days
# A period that the user is allowed to confirm their account before their
# token becomes invalid. For example, if set to 3.days, the user can confirm
# their account within 3 days after the mail was sent, but on the fourth day
# their account can't be confirmed with the token any more.
# Default is nil, meaning there is no restriction on how long a user can take
# before confirming their account.
# config.confirm_within = 3.days
# If true, requires any email changes to be confirmed (exactly the same way as
# initial account confirmation) to be applied. Requires additional unconfirmed_email
# db field (see migrations). Until confirmed, new email is stored in
# unconfirmed_email column, and copied to email column on successful confirmation.
config.reconfirmable = true
# Defines which key will be used when confirming an account
# config.confirmation_keys = [:email]
# ==> Configuration for :rememberable
# The time the user will be remembered without asking for credentials again.
# config.remember_for = 2.weeks
# Invalidates all the remember me tokens when the user signs out.
config.expire_all_remember_me_on_sign_out = true
# If true, extends the user's remember period when remembered via cookie.
# config.extend_remember_period = false
# Options to be passed to the created cookie. For instance, you can set
# secure: true in order to force SSL only cookies.
# config.rememberable_options = {}
# ==> Configuration for :validatable
# Range for password length.
config.password_length = 6..128
# Email regex used to validate email formats. It simply asserts that
# one (and only one) @ exists in the given string. This is mainly
# to give user feedback and not to assert the e-mail validity.
config.email_regexp = /\A[^@\s]+@[^@\s]+\z/
# ==> Configuration for :timeoutable
# The time you want to timeout the user session without activity. After this
# time the user will be asked for credentials again. Default is 30 minutes.
# config.timeout_in = 30.minutes
# ==> Configuration for :lockable
# Defines which strategy will be used to lock an account.
# :failed_attempts = Locks an account after a number of failed attempts to sign in.
# :none = No lock strategy. You should handle locking by yourself.
# config.lock_strategy = :failed_attempts
# Defines which key will be used when locking and unlocking an account
# config.unlock_keys = [:email]
# Defines which strategy will be used to unlock an account.
# :email = Sends an unlock link to the user email
# :time = Re-enables login after a certain amount of time (see :unlock_in below)
# :both = Enables both strategies
# :none = No unlock strategy. You should handle unlocking by yourself.
# config.unlock_strategy = :both
# Number of authentication tries before locking an account if lock_strategy
# is failed attempts.
# config.maximum_attempts = 20
# Time interval to unlock the account if :time is enabled as unlock_strategy.
# config.unlock_in = 1.hour
# Warn on the last attempt before the account is locked.
# config.last_attempt_warning = true
# ==> Configuration for :recoverable
#
# Defines which key will be used when recovering the password for an account
# config.reset_password_keys = [:email]
# Time interval you can reset your password with a reset password key.
# Don't put a too small interval or your users won't have the time to
# change their passwords.
config.reset_password_within = 6.hours
# When set to false, does not sign a user in automatically after their password is
# reset. Defaults to true, so a user is signed in automatically after a reset.
# config.sign_in_after_reset_password = true
# ==> Configuration for :encryptable
# Allow you to use another hashing or encryption algorithm besides bcrypt (default).
# You can use :sha1, :sha512 or algorithms from others authentication tools as
# :clearance_sha1, :authlogic_sha512 (then you should set stretches above to 20
# for default behavior) and :restful_authentication_sha1 (then you should set
# stretches to 10, and copy REST_AUTH_SITE_KEY to pepper).
#
# Require the `devise-encryptable` gem when using anything other than bcrypt
# config.encryptor = :sha512
# ==> Scopes configuration
# Turn scoped views on. Before rendering "sessions/new", it will first check for
# "users/sessions/new". It's turned off by default because it's slower if you
# are using only default views.
# config.scoped_views = false
# Configure the default scope given to Warden. By default it's the first
# devise role declared in your routes (usually :user).
# config.default_scope = :user
# Set this configuration to false if you want /users/sign_out to sign out
# only the current scope. By default, Devise signs out all scopes.
# config.sign_out_all_scopes = true
# ==> Navigation configuration
# Lists the formats that should be treated as navigational. Formats like
# :html, should redirect to the sign in page when the user does not have
# access, but formats like :xml or :json, should return 401.
#
# If you have any extra navigational formats, like :iphone or :mobile, you
# should add them to the navigational formats lists.
#
# The "*/*" below is required to match Internet Explorer requests.
# config.navigational_formats = ['*/*', :html]
# The default HTTP method used to sign out a resource. Default is :delete.
config.sign_out_via = :delete
# ==> OmniAuth
# Add a new OmniAuth provider. Check the wiki for more information on setting
# up on your models and hooks.
# config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo'
# ==> Warden configuration
# If you want to use other strategies, that are not supported by Devise, or
# change the failure app, you can configure them inside the config.warden block.
#
# config.warden do |manager|
# manager.intercept_401 = false
# manager.default_strategies(scope: :user).unshift :some_external_strategy
# end
# ==> Mountable engine configurations
# When using Devise inside an engine, let's call it `MyEngine`, and this engine
# is mountable, there are some extra configurations to be taken into account.
# The following options are available, assuming the engine is mounted as:
#
# mount MyEngine, at: '/my_engine'
#
# The router that invoked `devise_for`, in the example above, would be:
# config.router_name = :my_engine
#
# When using OmniAuth, Devise cannot automatically set OmniAuth path,
# so you need to do it manually. For the users scope, it would be:
# config.omniauth_path_prefix = '/my_engine/users/auth'
end
# Additional translations at https://github.com/plataformatec/devise/wiki/I18n
en:
devise:
confirmations:
confirmed: "Your email address has been successfully confirmed."
send_instructions: "You will receive an email with instructions for how to confirm your email address in a few minutes."
send_paranoid_instructions: "If your email address exists in our database, you will receive an email with instructions for how to confirm your email address in a few minutes."
failure:
already_authenticated: "You are already signed in."
inactive: "Your account is not activated yet."
invalid: "Invalid %{authentication_keys} or password."
locked: "Your account is locked."
last_attempt: "You have one more attempt before your account is locked."
not_found_in_database: "Invalid %{authentication_keys} or password."
timeout: "Your session expired. Please sign in again to continue."
unauthenticated: "You need to sign in or sign up before continuing."
unconfirmed: "You have to confirm your email address before continuing."
mailer:
confirmation_instructions:
subject: "Confirmation instructions"
reset_password_instructions:
subject: "Reset password instructions"
unlock_instructions:
subject: "Unlock instructions"
email_changed:
subject: "Email Changed"
password_change:
subject: "Password Changed"
omniauth_callbacks:
failure: "Could not authenticate you from %{kind} because \"%{reason}\"."
success: "Successfully authenticated from %{kind} account."
passwords:
no_token: "You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided."
send_instructions: "You will receive an email with instructions on how to reset your password in a few minutes."
send_paranoid_instructions: "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes."
updated: "Your password has been changed successfully. You are now signed in."
updated_not_active: "Your password has been changed successfully."
registrations:
destroyed: "Bye! Your account has been successfully cancelled. We hope to see you again soon."
signed_up: "Welcome! You have signed up successfully."
signed_up_but_inactive: "You have signed up successfully. However, we could not sign you in because your account is not yet activated."
signed_up_but_locked: "You have signed up successfully. However, we could not sign you in because your account is locked."
signed_up_but_unconfirmed: "A message with a confirmation link has been sent to your email address. Please follow the link to activate your account."
update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and follow the confirm link to confirm your new email address."
updated: "Your account has been updated successfully."
sessions:
signed_in: "Signed in successfully."
signed_out: "Signed out successfully."
already_signed_out: "Signed out successfully."
unlocks:
send_instructions: "You will receive an email with instructions for how to unlock your account in a few minutes."
send_paranoid_instructions: "If your account exists, you will receive an email with instructions for how to unlock it in a few minutes."
unlocked: "Your account has been unlocked successfully. Please sign in to continue."
errors:
messages:
already_confirmed: "was already confirmed, please try signing in"
confirmation_period_expired: "needs to be confirmed within %{period}, please request a new one"
expired: "has expired, please request a new one"
not_found: "not found"
not_locked: "was not locked"
not_saved:
one: "1 error prohibited this %{resource} from being saved:"
other: "%{count} errors prohibited this %{resource} from being saved:"
...@@ -2,4 +2,6 @@ Rails.application.routes.draw do ...@@ -2,4 +2,6 @@ Rails.application.routes.draw do
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
root 'static_pages#index' root 'static_pages#index'
resources :categories resources :categories
resources :products
devise_for :users
end end
...@@ -3,7 +3,7 @@ class CreateProducts < ActiveRecord::Migration[5.1] ...@@ -3,7 +3,7 @@ class CreateProducts < ActiveRecord::Migration[5.1]
create_table :products do |t| create_table :products do |t|
t.string :title t.string :title
t.text :description t.text :description
t.text :sku t.string :sku
t.decimal :price, precision: 8, scale: 2 t.decimal :price, precision: 8, scale: 2
t.references :category, index: true t.references :category, index: true
t.timestamps t.timestamps
......
class DeviseCreateUsers < ActiveRecord::Migration[5.1]
def change
create_table :users do |t|
## Database authenticatable
t.string :email, null: false, default: ""
t.string :encrypted_password, null: false, default: ""
## Recoverable
t.string :reset_password_token
t.datetime :reset_password_sent_at
## Rememberable
t.datetime :remember_created_at
## Trackable
t.integer :sign_in_count, default: 0, null: false
t.datetime :current_sign_in_at
t.datetime :last_sign_in_at
t.string :current_sign_in_ip
t.string :last_sign_in_ip
## Confirmable
# t.string :confirmation_token
# t.datetime :confirmed_at
# t.datetime :confirmation_sent_at
# t.string :unconfirmed_email # Only if using reconfirmable
## Lockable
# t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
# t.string :unlock_token # Only if unlock strategy is :email or :both
# t.datetime :locked_at
t.timestamps null: false
end
add_index :users, :email, unique: true
add_index :users, :reset_password_token, unique: true
# add_index :users, :confirmation_token, unique: true
# add_index :users, :unlock_token, unique: true
end
end
class AddUserRefToProducts < ActiveRecord::Migration[5.1]
def change
add_reference :products, :user, index: true
end
end
...@@ -10,24 +10,63 @@ ...@@ -10,24 +10,63 @@
# #
# 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: 20170606064132) do ActiveRecord::Schema.define(version: 20170608073720) do
create_table "categories", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t| create_table "categories", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
t.string "title" t.string "title"
t.text "description" t.text "description"
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"
t.text "sku" t.string "sku"
t.decimal "price", precision: 8, scale: 2 t.decimal "price", precision: 8, scale: 2
t.bigint "category_id" t.bigint "category_id"
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.string "image_url" t.string "image_url"
t.bigint "user_id"
t.index ["category_id"], name: "index_products_on_category_id" t.index ["category_id"], name: "index_products_on_category_id"
t.index ["user_id"], name: "index_products_on_user_id"
end
create_table "users", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at", null: false
t.datetime "updated_at", 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 ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end end
add_foreign_key "products", "categories" add_foreign_key "products", "users"
end end
...@@ -6,27 +6,39 @@ ...@@ -6,27 +6,39 @@
# movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }])
# Character.create(name: 'Luke', movie: movies.first) # Character.create(name: 'Luke', movie: movies.first)
# Delete all categories # Delete all existed users
User.destroy_all
# Create sample users
4.times do
User.create!(email: Faker::Internet.safe_email,
password: 'password',
password_confirmation: 'password')
end
# Delete all existed categories
Category.destroy_all Category.destroy_all
# Create Product Categories # Create Product Categories
4.times do |n| 4.times do |n|
title = "Category #{n}" Category.create!(title: "Category #{n + 1}",
desc = Faker::Lorem.paragraphs description: Faker::Lorem.paragraphs)
Category.create!(title: title, description: desc)
end end
# Create Products # Create Products
categories = Category.all categories = Category.all
80.times do 80.times do
categories.each do |cat| categories.each do |cat|
title = Faker::Commerce.product_name cat.products.create!(title: Faker::Commerce.product_name,
desc = Faker::Lorem.paragraphs description: Faker::Lorem.paragraphs,
sku = "u-#{rand(1..999)}" sku: "u-#{rand(1..999)}",
price = Faker::Commerce.price price: Faker::Commerce.price,
cat.products.create!(title: title, user_id: User.pluck(:id).shuffle[1])
description: desc,
sku: sku,
price: price)
end end
end end
\ No newline at end of file
# Create super user
User.create!(email: 'admin@example.com',
password: 'password',
password_confirmation: 'password',
super_admin: true)
\ No newline at end of file
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