diff --git a/.rubocop.yml b/.rubocop.yml index b9a307d..697a153 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -22,6 +22,7 @@ AllCops: - 'db/migrate/*.rb' - 'db/*.rb' - 'bin/**/*' + - 'config/environments/*.rb' Style/Documentation: Enabled: false diff --git a/Gemfile b/Gemfile index 747325d..e609eaa 100644 --- a/Gemfile +++ b/Gemfile @@ -24,7 +24,7 @@ gem "bootstrap4-kaminari-views" gem "config", "~> 2.0" gem "cocoon" gem "figaro" -gem "carrierwave", "~> 0.10.0" +gem "carrierwave" gem "mini_magick", "~> 4.3" group :development, :test do diff --git a/Gemfile.lock b/Gemfile.lock index f9c23ce..1ac352a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -50,7 +50,7 @@ GEM execjs bcrypt (3.1.13) bindex (0.8.1) - bootsnap (1.4.4) + bootsnap (1.4.5) msgpack (~> 1.0) bootstrap (4.3.1) autoprefixer-rails (>= 9.1.0) @@ -61,10 +61,9 @@ GEM rails (>= 3.1) builder (3.2.3) byebug (11.0.1) - carrierwave (0.10.0) - activemodel (>= 3.2.0) - activesupport (>= 3.2.0) - json (>= 1.7) + carrierwave (1.3.1) + activemodel (>= 4.0.0) + activesupport (>= 4.0.0) mime-types (>= 1.16) childprocess (1.0.1) rake (< 13.0) @@ -127,7 +126,7 @@ GEM dry-logic (~> 1.0, >= 1.0.2) erubi (1.8.0) execjs (2.7.0) - faker (2.1.2) + faker (2.2.0) i18n (>= 0.8) ffi (1.11.1) figaro (1.1.1) @@ -146,7 +145,6 @@ GEM rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) - json (2.2.0) kaminari (1.1.1) activesupport (>= 4.1.0) kaminari-actionview (= 1.1.1) @@ -181,7 +179,7 @@ GEM minitest (5.11.3) msgpack (1.3.1) mysql2 (0.5.2) - nio4r (2.4.0) + nio4r (2.5.1) nokogiri (1.10.4) mini_portile2 (~> 2.4.0) orm_adapter (0.5.0) @@ -268,7 +266,7 @@ GEM sprockets (>= 2.8, < 4.0) sprockets-rails (>= 2.0, < 4.0) tilt (>= 1.1, < 3) - sassc (2.1.0-x86_64-linux) + sassc (2.2.0) ffi (~> 1.9) sassc-rails (2.1.2) railties (>= 4.0.0) @@ -329,7 +327,7 @@ DEPENDENCIES bootstrap (~> 4.3.1) bootstrap4-kaminari-views byebug - carrierwave (~> 0.10.0) + carrierwave chromedriver-helper cocoon coffee-rails (~> 4.2) diff --git a/app/assets/images/backpack@2x.png b/app/assets/images/backpack@2x.png new file mode 100644 index 0000000..2cb56fd Binary files /dev/null and b/app/assets/images/backpack@2x.png differ diff --git a/app/assets/images/coins@2x.png b/app/assets/images/coins@2x.png new file mode 100644 index 0000000..ab4ff5e Binary files /dev/null and b/app/assets/images/coins@2x.png differ diff --git a/app/assets/images/location_2.png b/app/assets/images/location_2.png new file mode 100644 index 0000000..9c537c4 Binary files /dev/null and b/app/assets/images/location_2.png differ diff --git a/app/assets/images/location_6.png b/app/assets/images/location_6.png new file mode 100644 index 0000000..6511217 Binary files /dev/null and b/app/assets/images/location_6.png differ diff --git a/app/assets/images/location_7.png b/app/assets/images/location_7.png new file mode 100644 index 0000000..958e45d Binary files /dev/null and b/app/assets/images/location_7.png differ diff --git a/app/assets/images/location_8.png b/app/assets/images/location_8.png new file mode 100644 index 0000000..d62a667 Binary files /dev/null and b/app/assets/images/location_8.png differ diff --git a/app/assets/images/location_9.png b/app/assets/images/location_9.png new file mode 100644 index 0000000..e568b88 Binary files /dev/null and b/app/assets/images/location_9.png differ diff --git a/app/assets/images/nature.jpg b/app/assets/images/nature.jpg new file mode 100644 index 0000000..3014828 Binary files /dev/null and b/app/assets/images/nature.jpg differ diff --git a/app/assets/images/top-sales@2x.png b/app/assets/images/top-sales@2x.png new file mode 100644 index 0000000..fb4d283 Binary files /dev/null and b/app/assets/images/top-sales@2x.png differ diff --git a/app/assets/images/wallet@2x.png b/app/assets/images/wallet@2x.png new file mode 100644 index 0000000..e09089f Binary files /dev/null and b/app/assets/images/wallet@2x.png differ diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index d8a5a2a..885cfb4 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -1,2 +1,8 @@ //= require rails-ujs //= require activestorage +//= require cable +//= require jquery3 +//= require popper +//= require bootstrap +//= require custom +//= require toastr diff --git a/app/assets/javascripts/custom.js b/app/assets/javascripts/custom.js new file mode 100644 index 0000000..aa37640 --- /dev/null +++ b/app/assets/javascripts/custom.js @@ -0,0 +1,40 @@ +$(document).ready(function() { + var $headline = $('.headline'), + $inner = $('.inner'), + $nav = $('nav'), + navHeight = 75; + + $(window).scroll(function() { + var scrollTop = $(this).scrollTop(), + headlineHeight = $headline.outerHeight() - navHeight, + navOffset = $nav.offset().top; + + $headline.css({ + 'opacity': (1 - scrollTop / headlineHeight) + }); + + $inner.children().css({ + 'transform': 'translateY('+ scrollTop * 0.4 +'px)' + }); + if (navOffset > headlineHeight) { + $nav.addClass('scrolled'); + } else { + $nav.removeClass('scrolled'); + } + }); + + $(".preview-signup").change(function() { + readURL(this, '#img-prev-signup'); + }); +}); + +function readURL(f, previewId) { + if (f.files && f.files[0]) { + var reader = new FileReader(); + reader.onload = function (e) { + $(previewId) + .attr('src', e.target.result); + }; + reader.readAsDataURL(f.files[0]); + } +} diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index 3a76c14..d0dfcc5 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -1,3 +1,5 @@ /* + *= require custom +.*= require toastr *= require_self */ diff --git a/app/assets/stylesheets/custom.scss b/app/assets/stylesheets/custom.scss new file mode 100644 index 0000000..ee3ccb4 --- /dev/null +++ b/app/assets/stylesheets/custom.scss @@ -0,0 +1,572 @@ +@import 'bootstrap'; + +/* ---------------------------- +header +---------------------------- */ +@import url(https://fonts.googleapis.com/css?family=Open+Sans:300,400,700); +body { + margin: 0; + padding: 0; + font-family: 'Open Sans', sans-serif; + color: #222; + font-weight: 500; + font-size: 1rem; +} + +header { + background: #111; +} + +.headline { + position: relative; + height: 78px; + color: #fff; + text-align: center; + &:after { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + } +} + +.inner { + position: absolute; + top: 50%; + left: 50%; + opacity: 0; + z-index: 10; + transform: translateX(-50%) translateY(-50%); + animation: fade-in 0.75s 0.25s ease-in forwards; +} + +nav { + position: fixed; + width: 100%; + z-index: 10; + &.scrolled { + background: rgba(#111, 0.9); + } + ul { + float: right; + list-style: none; + padding: 25px; + margin: 0; + } + li { + float: left; + } + a { + font-size: 0.9em; + color: #fff; + text-decoration: none; + margin: 5px 0 0 20px; + display: block; + &:hover { + color: darken(#fff, 7%); + } + } +} + +.logo { + height: 75px; + float: left; + &:before, &:after { + position: absolute; + font-size: 7em; + font-weight: 300; + line-height: 0; + color: #fff; + top: 25px; + } + &:before { + content: '\00BB'; + left: 20px; + } + &:after { + content: '\00AB'; + left: 34px; + } +} + +section { + width: 100%; + margin: 25px auto; + padding: 20px; + max-width: 800px; + font-size: 1.2em; + line-height: 1.6em; + box-sizing: border-box; +} + +@keyframes fade-in { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } +} + +/* ---------------------------- +index +---------------------------- */ +.triptype { + height: 96px; + position: relative; + display: flex; + align-items: center; + overflow: hidden; + border-radius: 3px; + box-shadow: 0 2px 4px 0 rgba(0,0,0,.06); + border: 1px solid rgba(0,0,0,.1); + background-color: #fff; +} + +.triptype__link { + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + z-index: 1; +} + +.triptype__cover { + flex: 0 1 128px; + height: 100%; + position: relative; +} + +.triptype__title { + padding-left: 20px; + flex: 1 1; +} + +.bold, .coupon_action, .coupon_code { + font-weight: 700; +} + +.mb--0 { + margin-bottom: 0!important; +} + +.p--large { + font-size: 20px; +} + +.triptype__cover img { + position: absolute; + width: 100%; + height: 100%; + object-fit: cover; + top: 0; + left: 0; +} + +.slick-slide img { + display: block; +} + +.extra-bold{ + font-weight: bold; + font-size: 25px; +} + +.container.favorite-space { + max-width: calc(1520px + 5.6rem); + padding: 40px 2.8rem 40px; + margin: auto; +} + +.title { + padding: 12px 0; +} + +.title-location { + padding: 50px 0 0; +} + +.product__cover.image-tag{ + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + -o-object-fit: cover; + object-fit: cover; +} + +.product__cover{ + position: relative; + width: 100%; +} + +.product__image { + border-radius: .1875rem; + width: 100%; +} + +.image-tag { + border-style: none; + max-width: 100%; + vertical-align: middle; + height: auto; +} + +.product { + margin-top: 1rem; +} + +.is-relative { + position: relative; +} + +.product__content { + bottom: 1.5rem; + left: 1.5rem; +} + +.is-absolute { + position: absolute!important; +} + +.product__title { + margin-top: .75rem; + font-size: 1.5rem; + margin-bottom: .25rem; +} + +.white, .white-deep * { + color: #fff; +} + +.product__price { + color: hsla(0,0%,100%,.9); +} + +.product__price { + font-size: 1rem; +} + +.d-block { + display: block!important; +} + +/* ---------------------------- +footer +---------------------------- */ + +html, body{ + line-height: 1.5; +} + +.content{ + width: 100%; + margin: auto; + margin-bottom: 350px; /* Same height as footer */ +} + +.fixed_footer{ + width: 100%; + height: 111px; + background: #111; +} + +.fixed_footer p{ + color: #fff; + font-weight: 300; + font-size: 1.25em; + padding: 25px 0 40px 0; + text-align: center; +} + +/* ---------------------------- +footer social network +---------------------------- */ + +@import url(https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,200,300,600,700,900); + +$rhino: #323b40; +$rhinoMid: #4f585e; +$teal: #0096a1; +$tealMid: #0ebac7; +$red: #fc625c; +$amber: #fdbc40; +$green: #34c748; +$offWhite: #e9eaea; + +.social-media-links { + overflow: hidden; + padding-bottom: 4px; + text-align: center; + ul { + margin: 0; + padding: 7px; + } + li { + display: inline; + margin: 0; + padding: 0; + } + a { + border-bottom: 0px solid rgba(0,0,0,0.95); + border-radius: 4px; + box-shadow: inset 0 -3px 0 0 rgba(0,0,0,0), 0 6px 8px rgba(0,0,0,0), 0 24px 24px rgba(0,0,0,0), 0 36px 36px rgba(0,0,0,0), 0 64px 64px rgba(0,0,0,0), 0 64px 128px rgba(0,0,0,0), 0 120px 0 rgba(0,0,0,0), 0 86px 8px 6px rgba(0,0,0,0),; + display: inline-block; + height: 30px; + padding: 20px; + position: relative; + transition: .2s ease-in; + width: 30px; + svg { + left: 20px; + position: absolute; + top: 20px; + height: 30px; + width: 30px; + &.glow { + path, circle { + fill: rgba(0,0,0,0); + } + } + path, circle { + fill: $teal; + transition: .2s ease-in; + } + } + } + + a:hover { + transform: translateY(-4px); + box-shadow: inset 0 -3px 0 0 rgba(0,0,0,0.1), 0 6px 8px rgba(0,0,0,0.05), 0 24px 24px rgba(0,0,0,0.05), 0 36px 36px rgba(0,0,0,0.05), 0 64px 64px rgba(0,0,0,0.15), 0 64px 128px rgba(0,0,0,0.15), 0 86px 8px 6px fadeout($tealMid, 75), 0 83px 4px 0px fadeout($tealMid, 5); + svg { + &.glow { + filter: url('data:image/svg+xml;charset=utf-8,#filter'); + filter: blur(2px); + path, circle { + fill: fadeout($tealMid, 40); + } + } + path, circle { + fill: $tealMid; + } + } + } +} + +@media (min-width: 769px){ + .welcome { + padding: 3rem 0; + } +} + +.welcome { + background: linear-gradient(90deg,#f65e38 0,#f68a39 51%,#f65e38); + padding: 3rem 0 5rem; + color: #fff; +} + +.container--sm { + max-width: calc(1296px + 5.6rem); +} + +@media (min-width: 1200px){ + .container { + padding: 0 2.8rem; + } +} + +.container { + margin: auto; + padding: 0 1.25rem; +} + +@media (min-width: 992px) { + .container div div { + flex-basis: 58.33333%; + max-width: 58.33333%; + } +} + +.container div div { + flex-basis: 100%; + max-width: 100%; +} + +@media (min-width: 768px) { + .welcome__desc { + font-size: 1.125rem; + } +} + +.welcome__desc { + font-size: .95rem; + margin: 0; + + p { + margin: 0 0 1rem; + } +} + +h1, h2, h3, h4, h5, h6 { + font-weight: 900; + margin-top: 0; + line-height: 1.25; +} + +.main-content-sm { + max-width: calc(1296px + 5.6rem); +} + +@media (min-width: 1200px) { + .main-content { + padding: 0 2.8rem; + } +} + +.main-content { + margin: auto; + padding: 0 1.25rem; +} + +.cusrow { + flex-wrap: wrap; + box-sizing: border-box; + display: flex; + margin-left: -.5rem; + margin-right: -.5rem; +} + +@media (min-width: 992px) { + .first-md { + -ms-order: 1; + order: 1; + } +} + +@media (min-width: 992px) { + .cus-col { + flex-basis: 66.66667%; + max-width: 66.66667%; + } +} + +.cus-row-img { + flex-wrap: wrap; + box-sizing: border-box; + display: flex; + margin-left: -.5rem; + margin-right: -.5rem; +} + +@media (min-width: 992px) { + .cus-row-img .col-md-6 { + flex-basis: 50%; + max-width: 50%; + } +} + +@media (min-width: 768px) { + .cus-row-img .col-sm-6 { + flex-basis: 50%; + max-width: 50%; + } +} + +.media { + margin-top: 3rem; + flex-direction: column; + p { + margin: 0 0 1rem; + } +} + +.media__title { + margin: 1.5rem 0 .875rem; + font-size: 1.125rem; +} + + +.media__content { + color: #555; +} + +.m--0 { + margin: 0!important; +} + +@media (min-width: 990px) { + .account { + margin-top: -6.875rem; + } +} + +@media (min-width: 768px) { + .account { + padding: 2rem; + } +} + +@media (min-width: 992px) { + .cusrow .col-md-4 { + flex-basis: 33.33333%; + max-width: 33.33333%; + } +} + +.first-xs { + -ms-order: 2; + order: 2; +} + +.account { + padding: .975rem; + background: #fff; + box-shadow: 0 9px 46px 8px rgba(0,0,0,.02), 0 24px 38px 3px rgba(0,0,0,.04), 0 11px 15px -7px rgba(0,0,0,.05); + border-radius: .625rem; + margin-top: -3.375rem; +} + +.account__title { + font-size: 1.5rem; + margin-bottom: 2rem; +} + +.custom-checkbox.small label { + line-height: 1.5rem; +} + +.form-control-user { + font-size: 0.8rem; + border-radius: 10rem; + padding: 1.5rem 1rem; +} + +.btn-user { + font-size: 0.8rem; + border-radius: 10rem; + padding: 0.75rem 1rem; +} + +.avatar-default-signup { + width: 60px; + height: 60px; + border-radius: 50% +} + +.preview-signup { + position: relative; + top: 10px; + padding: 3px; + cursor: pointer; +} + +.control-label{ + font-weight: 700; +} + +#error_explanation { + color: red; +} diff --git a/app/assets/stylesheets/devise/application.scss b/app/assets/stylesheets/devise/application.scss index e3a98d4..ef8c0e6 100644 --- a/app/assets/stylesheets/devise/application.scss +++ b/app/assets/stylesheets/devise/application.scss @@ -10,3 +10,23 @@ #error_explanation { color: red; } + +.form-control-user { + font-size: 0.8rem; + border-radius: 10rem; + padding: 1.5rem 1rem; +} + +.btn-user { + font-size: 0.8rem; + border-radius: 10rem; + padding: 0.75rem 1rem; +} + +.custom-checkbox.small label { + line-height: 1.5rem; +} + +.control-label{ + font-weight: 700; +} diff --git a/app/assets/stylesheets/manager/custom.scss b/app/assets/stylesheets/manager/custom.scss index 3d7a26e..2f6a580 100644 --- a/app/assets/stylesheets/manager/custom.scss +++ b/app/assets/stylesheets/manager/custom.scss @@ -408,3 +408,7 @@ td.action{ width: 200px; float: left; } + +.control-label{ + font-weight: 700; +} diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 8b713bf..fe8ff94 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -4,16 +4,26 @@ class ApplicationController < ActionController::Base protect_from_forgery with: :exception add_flash_types :success, :danger, :warning, :info devise_group :user, contains: %i[admin member] + before_action :configure_permitted_parameters, if: :devise_controller? rescue_from ActiveRecord::RecordNotFound, ActionController::RoutingError do render file: "#{Rails.root}/public/404", layout: false, status: :not_found end def after_sign_in_path_for(resource) - stored_location_for(resource) || manager_root_path if resource.type == "Admin" + link = resource.type == "Admin" ? manager_root_path : root_path + stored_location_for(resource) || link end def after_sign_out_path_for(resource_or_scope) - new_admin_session_path if resource_or_scope == :admin + resource_or_scope == :admin ? new_admin_session_path : root_path + end + + protected + + def configure_permitted_parameters + devise_parameter_sanitizer.permit(:sign_up, keys: %i[name email address password password_confirmation avatar]) + devise_parameter_sanitizer + .permit(:account_update, keys: %i[name email address password password_confirmation avatar]) end end diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb new file mode 100644 index 0000000..a0ac1f8 --- /dev/null +++ b/app/controllers/home_controller.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +class HomeController < ApplicationController + def index; end +end diff --git a/app/controllers/manager/admins_controller.rb b/app/controllers/manager/admins_controller.rb index 54438db..63e25ad 100644 --- a/app/controllers/manager/admins_controller.rb +++ b/app/controllers/manager/admins_controller.rb @@ -14,18 +14,16 @@ def new def create @admin = Admin.new admin_params - @admin.avatar = params[:admin][:avatar] if @admin.save flash.now[:success] = t "messages.success.admins.create" else - @admin.avatar.purge_later flash.now[:danger] = t "messages.failed.admins.create" end end def update support_update @admin - if @admin.changed? || !params[:admin][:avatar].blank? + if @admin.changed? update_admin @admin else flash.now[:notice] = t("messages.notice.admins.not_edit", id: @admin.id) @@ -45,11 +43,11 @@ def load_admin def admin_params params.require(:admin).permit :email, :name, :address, - :password, :password_confirmation + :password, :password_confirmation, :avatar end def update_admin(admin) - if admin.save && admin.update_avatar(params[:admin][:avatar]) + if admin.save flash.now[:success] = t("messages.success.admins.update", id: admin.id) else flash.now[:danger] = t("messages.failed.admins.update", id: admin.id) @@ -69,7 +67,6 @@ def support_update(admin) def destroy_admin(admin) if !admin.flag? && admin.destroy - admin.avatar.purge_later flash[:success] = t("messages.success.admins.delete", id: admin.id) else flash[:danger] = t("messages.failed.admins.delete", id: admin.id) diff --git a/app/controllers/manager/members_controller.rb b/app/controllers/manager/members_controller.rb index 390544c..b3ae337 100644 --- a/app/controllers/manager/members_controller.rb +++ b/app/controllers/manager/members_controller.rb @@ -13,18 +13,16 @@ def new def create @member = Member.new member_params - @member.avatar = params[:member][:avatar] if @member.save flash.now[:success] = t "messages.success.members.create" else - @member.avatar.purge_later flash.now[:danger] = t "messages.failed.members.create" end end def update support_update @member - if @member.changed? || !params[:member][:avatar].blank? + if @member.changed? update_member @member else flash.now[:notice] = t("messages.notice.members.not_edit", id: @member.id) @@ -33,7 +31,6 @@ def update def destroy if @member.destroy - @member.avatar.purge_later flash[:success] = t("messages.success.members.delete", id: @member.id) else flash[:danger] = t("messages.failed.members.delete", id: @member.id) @@ -45,7 +42,7 @@ def destroy def member_params params.require(:member).permit :email, :name, :address, - :password, :password_confirmation + :password, :password_confirmation, :avatar end def load_member @@ -53,7 +50,7 @@ def load_member end def update_member(member) - if member.save && member.update_avatar(params[:member][:avatar]) + if member.save flash.now[:success] = t("messages.success.members.update", id: member.id) else flash.now[:danger] = t("messages.failed.members.update", id: member.id) diff --git a/app/controllers/passwords_controller.rb b/app/controllers/passwords_controller.rb new file mode 100644 index 0000000..02dd946 --- /dev/null +++ b/app/controllers/passwords_controller.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +class PasswordsController < Devise::PasswordsController + layout "application" +end diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb new file mode 100644 index 0000000..03648ae --- /dev/null +++ b/app/controllers/registrations_controller.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +class RegistrationsController < Devise::RegistrationsController + layout "application" +end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb new file mode 100644 index 0000000..ed1db4f --- /dev/null +++ b/app/controllers/sessions_controller.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +class SessionsController < Devise::SessionsController + layout "application" +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 338e5e5..6f9d73f 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -7,16 +7,16 @@ def full_title(page_title = "") end def avatar_member(member) - if member.avatar.attached? - image_tag member.avatar, class: "avatar-default-member", id: "img_member_prev" + if member.avatar? + image_tag member.avatar.url, class: "avatar-default-member", id: "img_member_prev" else image_tag "defaultavatar.jpeg", class: "avatar-default-member", id: "img_member_prev" end end def avatar_admin(admin) - if admin.avatar.attached? - image_tag admin.avatar, class: "avatar-default", id: "img_prev" + if admin.avatar? + image_tag admin.avatar.url, class: "avatar-default", id: "img_prev" else image_tag "defaultavatar.jpeg", class: "avatar-default", id: "img_prev" end diff --git a/app/models/application_record.rb b/app/models/application_record.rb index fb31f82..ff776d6 100644 --- a/app/models/application_record.rb +++ b/app/models/application_record.rb @@ -4,5 +4,5 @@ class ApplicationRecord < ActiveRecord::Base self.abstract_class = true scope :sort_by_name, -> { order name: :DESC } - scope :newest, ->{ order created_at: :DESC } + scope :newest, -> { order created_at: :DESC } end diff --git a/app/models/user.rb b/app/models/user.rb index 16c152f..bddf359 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -3,42 +3,18 @@ class User < ApplicationRecord attr_accessor :not_update_password - devise :database_authenticatable, :recoverable, :rememberable, :validatable, :timeoutable + devise :registerable, :database_authenticatable, :recoverable, :rememberable, :validatable, :timeoutable has_many :rooms validates :name, presence: true, length: { maximum: 45 } validates :address, presence: true, length: { maximum: 255 } validates :type, presence: true - validate :avatar_validate, if: -> { avatar.attached? } - has_one_attached :avatar + mount_uploader :avatar, AvatarUploader def timeout_in Settings.time_out.minutes end - def avatar_validate - return if avatar.blob.content_type.starts_with?(Settings.file_path) && avatar.blob.byte_size < Settings.max_size - - avatar.purge - errors.add(:avatar, :not_valid) - end - - def update_avatar(value) - return true unless value.present? - - if value.size > Settings.max_size - errors.add(:avatar, :size) - false - elsif !value.content_type.starts_with?(Settings.file_path) - errors.add(:avatar, :format) - false - else - avatar.purge_later - update_attribute(:avatar, value) - true - end - end - protected def password_required? diff --git a/app/models/voucher.rb b/app/models/voucher.rb index 9402895..8465467 100644 --- a/app/models/voucher.rb +++ b/app/models/voucher.rb @@ -14,8 +14,7 @@ class Voucher < ApplicationRecord def date_off_after_time_now return if date_off.blank? - if date_off.strftime("%m/%d/%Y %T") < Time.now.strftime("%m/%d/%Y %T") - errors.add(:date_off, "must be bigger than the current time") - end + return errors.add(:date_off, "must be bigger than the current time") if + date_off.strftime("%m/%d/%Y %T") < Time.now.strftime("%m/%d/%Y %T") end end diff --git a/app/uploaders/avatar_uploader.rb b/app/uploaders/avatar_uploader.rb new file mode 100644 index 0000000..c8b37c1 --- /dev/null +++ b/app/uploaders/avatar_uploader.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +class AvatarUploader < 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 + + def size_range + 1..5.megabytes + 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 diff --git a/app/views/home/index.html.slim b/app/views/home/index.html.slim new file mode 100644 index 0000000..a54dfbb --- /dev/null +++ b/app/views/home/index.html.slim @@ -0,0 +1,54 @@ +.container.favorite-space + .title + h2.extra-bold.p--giant Explore In Your Way + .row + .col-md-3 style="width: 308px;" + div + div style="width: 100%; display: inline-block;" + .triptype + = link_to '', "https://www.luxstay.com/vi/s/?property_type=31&property_type=1", + class: "triptype__link" + .triptype__cover + = image_tag "location_6" + .triptype__title + p.bold.p--large.mb--0 Căn Hộ + .col-md-3 style="width: 308px;" + div + div style="width: 100%; display: inline-block;" + .triptype + = link_to '', "https://www.luxstay.com/vi/s/?property_type=31&property_type=1", class: "triptype__link" + .triptype__cover + = image_tag "location_7" + .triptype__title + p.bold.p--large.mb--0 Biệt Thự + .col-md-3 style="width: 308px;" + div + div style="width: 100%; display: inline-block;" + .triptype + = link_to '', "https://www.luxstay.com/vi/s/?property_type=31&property_type=1", class: "triptype__link" + .triptype__cover + = image_tag "location_8" + .triptype__title + p.bold.p--large.mb--0 Nhà Riêng + .col-md-3 style="width: 308px;" + div + div style="width: 100%; display: inline-block;" + .triptype + = link_to '', "https://www.luxstay.com/vi/s/?property_type=31&property_type=1", class: "triptype__link" + .triptype__cover + = image_tag "location_9" + .triptype__title + p.bold.p--large.mb--0 Studio + .title-location + h2.extra-bold.p--giant Top Destinations + .row style="width: 246px;" + div + = link_to "#", style: "width: 100%; display: inline-block;" + .product.is-relative + .product__cover + = image_tag "location_2", class: "product__image image-tag" + .product__content.is-absolute.white-deep + .product__title.extra-bold Hanoi + span.product__price.d-block + b 2108 + | Listings diff --git a/app/views/layouts/_footer.html.slim b/app/views/layouts/_footer.html.slim new file mode 100644 index 0000000..18ce507 --- /dev/null +++ b/app/views/layouts/_footer.html.slim @@ -0,0 +1,40 @@ +footer.fixed_footer + .row + .col-md-2 + .logo + .col-md-7 + p Lorem ipsum dolor sit amet, consectetur adipisicing elit. + .col-md-3.social-media-links + ul + li + /! twitter + a href="#" + svg.glow viewbox=("0 0 30 30") xmlns="http://www.w3.org/2000/svg" + path.st0 d=("M9.5 27.1c11.2 0 17.4-9.3 17.4-17.4 0-0.3 0-0.5 0-0.8 1.2-0.9 2.2-1.9 3-3.2 -1.1 0.5-2.3 0.8-3.5 1 1.3-0.8 2.2-2 2.7-3.4 -1.2 0.7-2.5 1.2-3.9 1.5 -1.1-1.2-2.7-1.9-4.5-1.9 -3.4 0-6.1 2.7-6.1 6.1 0 0.5 0.1 0.9 0.2 1.4C9.7 10.1 5.2 7.7 2.2 4 1.7 4.9 1.4 6 1.4 7.1c0 2.1 1.1 4 2.7 5.1 -1 0-1.9-0.3-2.8-0.8 0 0 0 0.1 0 0.1 0 3 2.1 5.4 4.9 6 -0.5 0.1-1.1 0.2-1.6 0.2 -0.4 0-0.8 0-1.1-0.1 0.8 2.4 3 4.2 5.7 4.2 -2.1 1.6-4.7 2.6-7.6 2.6 -0.5 0-1 0-1.5-0.1C2.8 26.1 6 27.1 9.5 27.1") / + svg viewbox=("0 0 30 30") xmlns="http://www.w3.org/2000/svg" + path.st0 d=("M9.5 27.1c11.2 0 17.4-9.3 17.4-17.4 0-0.3 0-0.5 0-0.8 1.2-0.9 2.2-1.9 3-3.2 -1.1 0.5-2.3 0.8-3.5 1 1.3-0.8 2.2-2 2.7-3.4 -1.2 0.7-2.5 1.2-3.9 1.5 -1.1-1.2-2.7-1.9-4.5-1.9 -3.4 0-6.1 2.7-6.1 6.1 0 0.5 0.1 0.9 0.2 1.4C9.7 10.1 5.2 7.7 2.2 4 1.7 4.9 1.4 6 1.4 7.1c0 2.1 1.1 4 2.7 5.1 -1 0-1.9-0.3-2.8-0.8 0 0 0 0.1 0 0.1 0 3 2.1 5.4 4.9 6 -0.5 0.1-1.1 0.2-1.6 0.2 -0.4 0-0.8 0-1.1-0.1 0.8 2.4 3 4.2 5.7 4.2 -2.1 1.6-4.7 2.6-7.6 2.6 -0.5 0-1 0-1.5-0.1C2.8 26.1 6 27.1 9.5 27.1") / + li + /! facebook + a href="#" + svg.glow viewbox=("0 0 30 30") xmlns="http://www.w3.org/2000/svg" + path.st0 d=("M28.3 0.1H1.7c-0.9 0-1.6 0.7-1.6 1.6v26.5c0 0.9 0.7 1.6 1.6 1.6H16V18.4h-3.9v-4.5H16v-3.3c0-3.9 2.4-5.9 5.8-5.9 1.6 0 3.1 0.1 3.5 0.2v4l-2.4 0c-1.9 0-2.2 0.9-2.2 2.2v2.9h4.5l-0.6 4.5h-3.9v11.5h7.6c0.9 0 1.6-0.7 1.6-1.6V1.7C29.9 0.8 29.2 0.1 28.3 0.1z") / + svg viewbox=("0 0 30 30") xmlns="http://www.w3.org/2000/svg" + path.st0 d=("M28.3 0.1H1.7c-0.9 0-1.6 0.7-1.6 1.6v26.5c0 0.9 0.7 1.6 1.6 1.6H16V18.4h-3.9v-4.5H16v-3.3c0-3.9 2.4-5.9 5.8-5.9 1.6 0 3.1 0.1 3.5 0.2v4l-2.4 0c-1.9 0-2.2 0.9-2.2 2.2v2.9h4.5l-0.6 4.5h-3.9v11.5h7.6c0.9 0 1.6-0.7 1.6-1.6V1.7C29.9 0.8 29.2 0.1 28.3 0.1z") / + li + /! youtube + a href="#" + svg.glow viewbox=("0 0 30 30") xmlns="http://www.w3.org/2000/svg" + path.st0 d=("M29.7 9c0 0-0.3-2.1-1.2-3 -1.1-1.2-2.4-1.2-3-1.3C21.3 4.5 15 4.5 15 4.5h0c0 0-6.3 0-10.5 0.3C3.9 4.8 2.6 4.8 1.5 6 0.6 6.9 0.3 9 0.3 9S0 11.4 0 13.9v2.3C0 18.6 0.3 21 0.3 21s0.3 2.1 1.2 3c1.1 1.2 2.6 1.2 3.3 1.3C7.2 25.5 15 25.6 15 25.6s6.3 0 10.5-0.3c0.6-0.1 1.9-0.1 3-1.3 0.9-0.9 1.2-3 1.2-3s0.3-2.4 0.3-4.9v-2.3C30 11.4 29.7 9 29.7 9zM11.9 18.9l0-8.4 8.1 4.2L11.9 18.9z") / + svg viewbox=("0 0 30 30") xmlns="http://www.w3.org/2000/svg" + path.st0 d=("M29.7 9c0 0-0.3-2.1-1.2-3 -1.1-1.2-2.4-1.2-3-1.3C21.3 4.5 15 4.5 15 4.5h0c0 0-6.3 0-10.5 0.3C3.9 4.8 2.6 4.8 1.5 6 0.6 6.9 0.3 9 0.3 9S0 11.4 0 13.9v2.3C0 18.6 0.3 21 0.3 21s0.3 2.1 1.2 3c1.1 1.2 2.6 1.2 3.3 1.3C7.2 25.5 15 25.6 15 25.6s6.3 0 10.5-0.3c0.6-0.1 1.9-0.1 3-1.3 0.9-0.9 1.2-3 1.2-3s0.3-2.4 0.3-4.9v-2.3C30 11.4 29.7 9 29.7 9zM11.9 18.9l0-8.4 8.1 4.2L11.9 18.9z") / + li + /! instagram + a href="#" + svg.glow viewbox=("0 0 30 30") xmlns="http://www.w3.org/2000/svg" + path.st0 d=("M15 2.8c4 0 4.4 0 6 0.1 1.4 0.1 2.2 0.3 2.8 0.5 0.7 0.3 1.2 0.6 1.7 1.1 0.5 0.5 0.8 1 1.1 1.7C26.8 6.8 27 7.6 27.1 9c0.1 1.6 0.1 2 0.1 6s0 4.4-0.1 6c-0.1 1.4-0.3 2.2-0.5 2.8 -0.3 0.7-0.6 1.2-1.1 1.7 -0.5 0.5-1 0.8-1.7 1.1 -0.5 0.2-1.3 0.4-2.8 0.5 -1.6 0.1-2 0.1-6 0.1s-4.4 0-6-0.1c-1.4-0.1-2.2-0.3-2.8-0.5 -0.7-0.3-1.2-0.6-1.7-1.1 -0.5-0.5-0.8-1-1.1-1.7C3.2 23.2 3 22.4 2.9 21c-0.1-1.6-0.1-2-0.1-6s0-4.4 0.1-6C3 7.6 3.2 6.8 3.4 6.2 3.7 5.5 4 5.1 4.5 4.5c0.5-0.5 1-0.8 1.7-1.1C6.8 3.2 7.6 3 9 2.9 10.6 2.8 11 2.8 15 2.8M15 0.2c-4 0-4.5 0-6.1 0.1C7.3 0.3 6.2 0.6 5.3 0.9c-1 0.4-1.8 0.9-2.6 1.7C1.8 3.5 1.3 4.3 0.9 5.3c-0.4 0.9-0.6 2-0.7 3.6C0.2 10.5 0.1 11 0.1 15c0 4 0 4.5 0.1 6.1 0.1 1.6 0.3 2.7 0.7 3.6 0.4 1 0.9 1.8 1.7 2.6 0.8 0.8 1.7 1.3 2.6 1.7 0.9 0.4 2 0.6 3.6 0.7 1.6 0.1 2.1 0.1 6.1 0.1s4.5 0 6.1-0.1c1.6-0.1 2.7-0.3 3.6-0.7 1-0.4 1.8-0.9 2.6-1.7 0.8-0.8 1.3-1.7 1.7-2.6 0.4-0.9 0.6-2 0.7-3.6 0.1-1.6 0.1-2.1 0.1-6.1s0-4.5-0.1-6.1c-0.1-1.6-0.3-2.7-0.7-3.6 -0.4-1-0.9-1.8-1.7-2.6 -0.8-0.8-1.7-1.3-2.6-1.7 -0.9-0.4-2-0.6-3.6-0.7C19.5 0.2 19 0.2 15 0.2L15 0.2z") / + path.st0 d=("M15 7.4c-4.2 0-7.6 3.4-7.6 7.6s3.4 7.6 7.6 7.6 7.6-3.4 7.6-7.6S19.2 7.4 15 7.4zM15 20c-2.7 0-5-2.2-5-5s2.2-5 5-5c2.7 0 5 2.2 5 5S17.7 20 15 20z") / + circle.st0 cx="22.9" cy="7.1" r="1.8" / + svg viewbox=("0 0 30 30") xmlns="http://www.w3.org/2000/svg" + path.st0 d=("M15 2.8c4 0 4.4 0 6 0.1 1.4 0.1 2.2 0.3 2.8 0.5 0.7 0.3 1.2 0.6 1.7 1.1 0.5 0.5 0.8 1 1.1 1.7C26.8 6.8 27 7.6 27.1 9c0.1 1.6 0.1 2 0.1 6s0 4.4-0.1 6c-0.1 1.4-0.3 2.2-0.5 2.8 -0.3 0.7-0.6 1.2-1.1 1.7 -0.5 0.5-1 0.8-1.7 1.1 -0.5 0.2-1.3 0.4-2.8 0.5 -1.6 0.1-2 0.1-6 0.1s-4.4 0-6-0.1c-1.4-0.1-2.2-0.3-2.8-0.5 -0.7-0.3-1.2-0.6-1.7-1.1 -0.5-0.5-0.8-1-1.1-1.7C3.2 23.2 3 22.4 2.9 21c-0.1-1.6-0.1-2-0.1-6s0-4.4 0.1-6C3 7.6 3.2 6.8 3.4 6.2 3.7 5.5 4 5.1 4.5 4.5c0.5-0.5 1-0.8 1.7-1.1C6.8 3.2 7.6 3 9 2.9 10.6 2.8 11 2.8 15 2.8M15 0.2c-4 0-4.5 0-6.1 0.1C7.3 0.3 6.2 0.6 5.3 0.9c-1 0.4-1.8 0.9-2.6 1.7C1.8 3.5 1.3 4.3 0.9 5.3c-0.4 0.9-0.6 2-0.7 3.6C0.2 10.5 0.1 11 0.1 15c0 4 0 4.5 0.1 6.1 0.1 1.6 0.3 2.7 0.7 3.6 0.4 1 0.9 1.8 1.7 2.6 0.8 0.8 1.7 1.3 2.6 1.7 0.9 0.4 2 0.6 3.6 0.7 1.6 0.1 2.1 0.1 6.1 0.1s4.5 0 6.1-0.1c1.6-0.1 2.7-0.3 3.6-0.7 1-0.4 1.8-0.9 2.6-1.7 0.8-0.8 1.3-1.7 1.7-2.6 0.4-0.9 0.6-2 0.7-3.6 0.1-1.6 0.1-2.1 0.1-6.1s0-4.5-0.1-6.1c-0.1-1.6-0.3-2.7-0.7-3.6 -0.4-1-0.9-1.8-1.7-2.6 -0.8-0.8-1.7-1.3-2.6-1.7 -0.9-0.4-2-0.6-3.6-0.7C19.5 0.2 19 0.2 15 0.2L15 0.2z") / + path.st0 d=("M15 7.4c-4.2 0-7.6 3.4-7.6 7.6s3.4 7.6 7.6 7.6 7.6-3.4 7.6-7.6S19.2 7.4 15 7.4zM15 20c-2.7 0-5-2.2-5-5s2.2-5 5-5c2.7 0 5 2.2 5 5S17.7 20 15 20z") / + circle.st0 cx="22.9" cy="7.1" r="1.8" / diff --git a/app/views/layouts/_header.html.slim b/app/views/layouts/_header.html.slim new file mode 100644 index 0000000..9ff7b35 --- /dev/null +++ b/app/views/layouts/_header.html.slim @@ -0,0 +1,13 @@ +nav + = link_to "", root_path, class: "logo" + ul + li = link_to "item1", "#" + - if member_signed_in? + li = link_to t(".signout"), destroy_member_session_path, method: :delete + - else + li = link_to t(".signup"), new_member_registration_path + li = link_to t(".signin"), new_member_session_path + li = link_to "item4", "#" +header + .headline + .inner diff --git a/app/views/layouts/application.html.slim b/app/views/layouts/application.html.slim index 0910fd6..155ca95 100644 --- a/app/views/layouts/application.html.slim +++ b/app/views/layouts/application.html.slim @@ -5,7 +5,11 @@ html | BookingHomestays = csrf_meta_tags = csp_meta_tag - = stylesheet_link_tag 'application', media: 'all' - = javascript_include_tag 'application' + = stylesheet_link_tag "application", media: "all" + = javascript_include_tag "application" body - = yield + = render "layouts/header" + = render "layouts/flashmessages" + main.content + = yield + = render "layouts/footer" diff --git a/app/views/layouts/sessions.html.slim b/app/views/layouts/sessions.html.slim index 09994ad..8460095 100644 --- a/app/views/layouts/sessions.html.slim +++ b/app/views/layouts/sessions.html.slim @@ -9,5 +9,5 @@ html = javascript_include_tag "devise/application" body.bg-gradient-primary .container - = render "layouts/flashmessages",flash: flash + = render "layouts/flashmessages" = yield diff --git a/app/views/manager/admins/_admin.html.slim b/app/views/manager/admins/_admin.html.slim index a646758..ffd0f83 100644 --- a/app/views/manager/admins/_admin.html.slim +++ b/app/views/manager/admins/_admin.html.slim @@ -2,8 +2,8 @@ tr.data-user td = admin.id td - - if admin.avatar.attached? - = image_tag admin.avatar, class: "show-avatar-index" + - if admin.avatar? + = image_tag admin.avatar.url, class: "show-avatar-index" - else = image_tag "defaultavatar.jpeg", class: "show-avatar-index" = admin.name diff --git a/app/views/manager/admins/update.js.slim b/app/views/manager/admins/update.js.slim index b4ff71f..7d14a5a 100644 --- a/app/views/manager/admins/update.js.slim +++ b/app/views/manager/admins/update.js.slim @@ -12,3 +12,6 @@ $("#admin-modal").modal("hide") $(".data-admin").html("#{j(render @admins)}") toastr.success("#{flash[:success]}"); + - if current_admin.id == @admin.id + | + $('#load-current').attr("src","#{@admin.avatar}"); diff --git a/app/views/manager/members/_form.html.slim b/app/views/manager/members/_form.html.slim index 46e03ec..34a8530 100644 --- a/app/views/manager/members/_form.html.slim +++ b/app/views/manager/members/_form.html.slim @@ -7,10 +7,7 @@ hr .form-group.row[style="padding: 25px"] .col-sm-3 - - if member.avatar.attached? - = image_tag member.avatar, class: "avatar-default-member", id: "img_member_prev" - - else - = image_tag "defaultavatar.jpeg", class: "avatar-default-member", id: "img_member_prev" + = avatar_member member .prev.col-sm-9 = f.file_field :avatar, class: "form-control preview_member" .form-group diff --git a/app/views/manager/members/_member.html.slim b/app/views/manager/members/_member.html.slim index 8d8bde1..f6ca66c 100644 --- a/app/views/manager/members/_member.html.slim +++ b/app/views/manager/members/_member.html.slim @@ -2,8 +2,8 @@ tr.data-user td = member.id td - - if member.avatar.attached? - = image_tag member.avatar, class: "show-avatar-index" + - if member.avatar? + = image_tag member.avatar.url, class: "show-avatar-index" - else = image_tag "defaultavatar.jpeg", class: "show-avatar-index" = member.name diff --git a/app/views/manager/passwords/edit.html.slim b/app/views/manager/passwords/edit.html.slim index 3bfdc2a..f23490b 100644 --- a/app/views/manager/passwords/edit.html.slim +++ b/app/views/manager/passwords/edit.html.slim @@ -8,23 +8,23 @@ .p-5 .text-center h1.h4.text-gray-900.mb-2 - | Change your password + = t ".title" p.mb-4 - | Now, You can enter a new password for your account! + = t ".description" = form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| = render "shared/manager/error_messages", object: resource = f.hidden_field :reset_password_token .form-group - = f.label :password, "New password" + = f.label :password, t(".pass_new"), class: "control-label" - if @minimum_password_length em - | (#{@minimum_password_length} characters minimum) + = t ".valid_pass", num: "#{@minimum_password_length}" br/ - = f.password_field :password, autofocus: true, autocomplete: "new-password", class: "form-control" + = f.password_field :password, autofocus: true, autocomplete: "new-password", class: "form-control form-control-user" .form-group - = f.label :password_confirmation, "Confirm new password" - = f.password_field :password_confirmation, autocomplete: "off", class: "form-control" + = f.label :password_confirmation, t(".pass_confirm"), class: "control-label" + = f.password_field :password_confirmation, autocomplete: "off", class: "form-control form-control-user" .actions - = f.submit "Change my password", class: "btn btn-primary btn-block" + = f.submit t(".change_btn"), class: "btn btn-primary btn-user btn-block" .text-center - = link_to "Already have an account? Login!", new_admin_session_path + = link_to t(".login_link"), new_admin_session_path, class: "small" diff --git a/app/views/manager/passwords/new.html.slim b/app/views/manager/passwords/new.html.slim index c725898..f0281e8 100644 --- a/app/views/manager/passwords/new.html.slim +++ b/app/views/manager/passwords/new.html.slim @@ -8,15 +8,15 @@ .p-5 .text-center h1.h4.text-gray-900.mb-2 - | Forgot Your Password? + = t ".title" p.mb-4 - | We get it, stuff happens. Just enter your email address below and we'll send you a link to reset your password! + = t ".description" = form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }, class: "user") do |f| = render "shared/manager/error_messages", object: resource .form-group - = f.label :email - = f.email_field :email, autofocus: true, autocomplete: "email", class: "form-control" + = f.label :email, class: "control-label" + = f.email_field :email, autofocus: true, autocomplete: "email", class: "form-control form-control-user" .actions - = f.submit "Reset Password", class: "btn btn-primary btn-block" + = f.submit t(".reset_btn"), class: "btn btn-primary btn-user btn-block" .text-center - = link_to "Already have an account? Login!", new_admin_session_path + = link_to t(".login_link"), new_admin_session_path, class: "small" diff --git a/app/views/manager/sessions/new.html.slim b/app/views/manager/sessions/new.html.slim index c48b9d0..6320b9f 100644 --- a/app/views/manager/sessions/new.html.slim +++ b/app/views/manager/sessions/new.html.slim @@ -7,13 +7,14 @@ .col-lg-6 .p-5 .text-center - h1.h4.text-gray-900.mb-4 Welcome Back! - = form_for(resource, as: resource_name, url: session_path(resource_name), class: "user") do |f| + h1.h4.text-gray-900.mb-4 + = t ".title" + = form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| .form-group - = f.label :email + = f.label :email, class: "control-label" = f.email_field :email, autofocus: true, autocomplete: "email", class: "form-control form-control-user", placeholder: "Enter Email Address...", id: "exampleInputEmail" .form-group - = f.label :password + = f.label :password, class: "control-label" = f.password_field :password, autocomplete: "current-password", class: "form-control form-control-user", placeholder: "Password", id: "exampleInputPassword" - if devise_mapping.rememberable? .form-group @@ -21,7 +22,6 @@ = f.check_box :remember_me, id: "customCheck", class: "custom-control-input" = f.label :remember_me, class: "custom-control-label", style: "padding: 2px", for: "customCheck" .actions - = f.submit "Log in", class: "btn btn-primary btn-user btn-block custom-btn-login" + = f.submit t(".login_btn"), class: "btn btn-primary btn-user btn-block custom-btn-login" .text-center - - if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' - = link_to "Forgot your password?", new_password_path(resource_name), class: "small" + = link_to t(".forgot_link"), new_password_path(resource_name), class: "small" diff --git a/app/views/passwords/_edit_form.html.slim b/app/views/passwords/_edit_form.html.slim new file mode 100644 index 0000000..9badd7d --- /dev/null +++ b/app/views/passwords/_edit_form.html.slim @@ -0,0 +1,23 @@ +.account + .text-center + h1.h4.text-gray-900.mb-2 + = t ".title" + p.mb-4 + = t ".description" + = form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| + = render "shared/manager/error_messages", object: resource + = f.hidden_field :reset_password_token + .form-group + = f.label :password, "New password", class: "control-label" + - if @minimum_password_length + em + = t ".valid_pass", num: "#{@minimum_password_length}" + br/ + = f.password_field :password, autofocus: true, autocomplete: "new-password", class: "form-control form-control-user" + .form-group + = f.label :password_confirmation, "Confirm new password", class: "control-label" + = f.password_field :password_confirmation, autocomplete: "off", class: "form-control form-control-user" + .actions + = f.submit t(".change_btn"), class: "btn btn-primary btn-user btn-block" + .text-center + = link_to t(".login_link"), new_member_session_path, class: "small" diff --git a/app/views/passwords/_new_form.html.slim b/app/views/passwords/_new_form.html.slim new file mode 100644 index 0000000..e258542 --- /dev/null +++ b/app/views/passwords/_new_form.html.slim @@ -0,0 +1,15 @@ +.account + .text-center + h1.h4.text-gray-900.mb-2 + = t ".title" + p.mb-4 + = t ".description" + = form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }, class: "user") do |f| + = render "shared/manager/error_messages", object: resource + .form-group + = f.label :email, class: "control-label" + = f.email_field :email, autofocus: true, autocomplete: "email", class: "form-control form-control-user" + .actions + = f.submit t(".reset_btn"), class: "btn btn-primary btn-user btn-block" + .text-center + = link_to t(".login_link"), new_member_session_path, class: "small" diff --git a/app/views/passwords/edit.html.slim b/app/views/passwords/edit.html.slim new file mode 100644 index 0000000..c65d7c6 --- /dev/null +++ b/app/views/passwords/edit.html.slim @@ -0,0 +1 @@ += render partial: "shared/view_content", locals: { yield_content: render(partial: "edit_form") } diff --git a/app/views/passwords/new.html.slim b/app/views/passwords/new.html.slim new file mode 100644 index 0000000..abab4a9 --- /dev/null +++ b/app/views/passwords/new.html.slim @@ -0,0 +1 @@ += render partial: "shared/view_content", locals: { yield_content: render(partial: "new_form") } diff --git a/app/views/registrations/_new_form.html.slim b/app/views/registrations/_new_form.html.slim new file mode 100644 index 0000000..cef14c0 --- /dev/null +++ b/app/views/registrations/_new_form.html.slim @@ -0,0 +1,34 @@ +.account + = form_for(resource, as: resource_name, url: registration_path(resource_name), class: "user") do |f| + h3 style="text-align: center" + = t ".title" + = render "shared/manager/error_messages", object: resource + hr + .form-group.row[style="padding: 25px"] + .col-sm-3 + = image_tag "defaultavatar.jpeg", class: "avatar-default-signup", id: "img-prev-signup" + .col-sm-9 + = f.file_field :avatar, class: "form-control preview-signup" + = f.hidden_field :avatar_cache + .form-group + = f.label :name, class: "control-label" + = f.text_field :name, class: "form-control form-control-user", required: true + .form-group + = f.label :email, class: "control-label" + = f.text_field :email, class: "form-control form-control-user", required: true + .form-group + = f.label :address, class: "control-label" + = f.text_field :address, class: "form-control form-control-user", required: true + .form-group + = f.label :password, class: "control-label" + - if @minimum_password_length + span style="color: #999" + = t ".valid_pass", num: "#{@minimum_password_length}" + = f.password_field :password, class: "form-control form-control-user", required: true + .form-group + = f.label :password_confirmation, class: "control-label" + = f.password_field :password_confirmation, class: "form-control form-control-user", required: true + .actions + = f.submit t(".signup_btn"), class: "btn btn-primary btn-user btn-block" + .text-center + = link_to t(".login_link"), new_member_session_path, class: "small" diff --git a/app/views/registrations/new.html.slim b/app/views/registrations/new.html.slim new file mode 100644 index 0000000..abab4a9 --- /dev/null +++ b/app/views/registrations/new.html.slim @@ -0,0 +1 @@ += render partial: "shared/view_content", locals: { yield_content: render(partial: "new_form") } diff --git a/app/views/sessions/_new_form.html.slim b/app/views/sessions/_new_form.html.slim new file mode 100644 index 0000000..54537cc --- /dev/null +++ b/app/views/sessions/_new_form.html.slim @@ -0,0 +1,19 @@ +.account + = form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| + .form-group + = f.label :email, class: "control-label" + = f.email_field :email, autofocus: true, autocomplete: "email", class: "form-control form-control-user", placeholder: "Enter Email Address...", id: "exampleInputEmail" + .form-group + = f.label :password, class: "control-label" + = f.password_field :password, autocomplete: "current-password", class: "form-control form-control-user", placeholder: "Password", id: "exampleInputPassword" + - if devise_mapping.rememberable? + .form-group + .custom-control.custom-checkbox.small + = f.check_box :remember_me, id: "customCheck", class: "custom-control-input" + = f.label :remember_me, class: "custom-control-label", style: "padding: 2px", for: "customCheck" + .actions + = f.submit t(".login_btn"), class: "btn btn-primary btn-user btn-block custom-btn-login" + .text-center + = link_to t(".forgot_link"), new_password_path(resource_name), class: "small" + br + = link_to t(".signup_link"), new_member_registration_path, class: "small" diff --git a/app/views/sessions/new.html.slim b/app/views/sessions/new.html.slim new file mode 100644 index 0000000..abab4a9 --- /dev/null +++ b/app/views/sessions/new.html.slim @@ -0,0 +1 @@ += render partial: "shared/view_content", locals: { yield_content: render(partial: "new_form") } diff --git a/app/views/shared/_view_content.html.slim b/app/views/shared/_view_content.html.slim new file mode 100644 index 0000000..802ab35 --- /dev/null +++ b/app/views/shared/_view_content.html.slim @@ -0,0 +1,43 @@ +.welcome + .container.container--sm + .row + .col-md-7.col-xs-12 + h1.welcome__title + = t ".welcome_title" + p.welcome__desc + = t ".welcome_des" +.section + .main-content.main-content-sm + .row.cusrow + .col-md-8.col-xs-12.last-xs.first-md.cus-col + .row.cus-row-img + .col-md-6.col-sm-6.col-xs-12 + .media + = image_tag("coins@2x.png", size: "65x70") + h3.media__title + = t ".lux_title" + p.media__content.m--0 + = t ".lux_des" + .col-md-6.col-sm-6.col-xs-12 + .media + = image_tag("top-sales@2x.png", size: "55x70") + h3.media__title + = t ".smart_title" + p.media__content.m--0 + = t ".smart_des" + .col-md-6.col-sm-6.col-xs-12 + .media + = image_tag("wallet@2x.png", size: "60x70") + h3.media__title + = t ".simple_title" + p.media__content.m--0 + = t ".simple_des" + .col-md-6.col-sm-6.col-xs-12 + .media + = image_tag("backpack@2x.png", size: "55x70") + h3.media__title + = t ".promo_title" + p.media__content.m--0 + = t ".promo_des" + .col-md-4.col-xs-12.first-xs + = yield_content diff --git a/app/views/shared/manager/_top.html.slim b/app/views/shared/manager/_top.html.slim index 18e3889..39c043a 100644 --- a/app/views/shared/manager/_top.html.slim +++ b/app/views/shared/manager/_top.html.slim @@ -8,8 +8,8 @@ nav.navbar.navbar-expand.navbar-light.bg-white.topbar.mb-4.static-top.shadow .custom-dropdown = link_to("#", class: "account", title: "#{current_admin.name}", "data-toggle": "tooltip", "data-placement": "left") do span = truncate(current_admin.name, length: 10) - - if current_admin.avatar.attached? - = image_tag current_admin.avatar, class: "profile-circle" + - if current_admin.avatar? + = image_tag current_admin.avatar.url, class: "profile-circle", id: "load-current" - else = image_tag "defaultavatar.jpeg", class: "profile-circle" .custom-submenu[style="display: none; "] diff --git a/config/environments/development.rb b/config/environments/development.rb index ecc38ed..9975caa 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -50,15 +50,10 @@ config.action_mailer.delivery_method = :smtp # SMTP settings for gmail config.action_mailer.smtp_settings = { - :address => ENV["ADDRESS"], - :port => 587, - :user_name => ENV["GMAIL_USERNAME"], - :password => ENV["GMAIL_PASSWORD"], - :authentication => "plain", - :enable_starttls_auto => true + address: ENV["ADDRESS"], port: 587, user_name: ENV["GMAIL_USERNAME"], + password: ENV["GMAIL_PASSWORD"], authentication: "plain", enable_starttls_auto: true } - # Debug mode disables concatenation and preprocessing of assets. # This option may cause significant delays in view rendering with a large # number of complex assets. diff --git a/config/environments/production.rb b/config/environments/production.rb index 3f205bf..9ab5048 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -67,19 +67,14 @@ config.action_mailer.perform_caching = false - config.action_mailer.default_url_options = { :host => "example.host.com" } + config.action_mailer.default_url_options = { host: "example.host.com" } config.action_mailer.perform_caching = false config.action_mailer.delivery_method = :smtp # SMTP settings for gmail config.action_mailer.smtp_settings = { - :address => ENV["ADDRESS"], - :port => 587, - :domain => ENV["DOMAIN"], - :user_name => ENV["GMAIL_USERNAME"], - :password => ENV["GMAIL_PASSWORD"], - :authentication => "plain", - :enable_starttls_auto => true + address: ENV["ADDRESS"], port: 587, domain: ENV["DOMAIN"], user_name: ENV["GMAIL_USERNAME"], + password: ENV["GMAIL_PASSWORD"], authentication: "plain", enable_starttls_auto: true } # Ignore bad email addresses and do not raise email delivery errors. # Set this to true and configure the email server for immediate delivery to raise delivery errors. diff --git a/config/locales/en.yml b/config/locales/en.yml index 69b68ee..d323811 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -3,6 +3,10 @@ en: sure: "Are you sure?" layouts: + header: + signup: "Sign Up" + signin: "Sign In" + signout: "Sign Out" manager: title: "Booking Homestays" messages: @@ -58,7 +62,41 @@ en: logout: "Logout" settings: "Settings" profile: "Profile" + view_content: + welcome_title: "Sign up for more promotions and benefits" + welcome_des: "Swift, convenient, and safe. Sign up now for more interests." + lux_title: "Coin" + lux_des: "Earn Coins for each successful booking and use them for your next trips." + smart_title: "Smart functions" + smart_des: "Check-in and check your receipts offline. Refund faster and change calendar easier." + simple_title: "Simple payment" + simple_des: "Save card information in My Cards, much safer and convenient for the next payment." + promo_title: "New promotions every day" + promo_des: "Update promotions from Booking and choose great accommodation at your budget." hello: "Hello world" + sessions: + new_form: + login_btn: "Log in" + forgot_link: "Forgot your password?" + signup_link: "Sign Up" + registrations: + new_form: + title: "Sign Up" + valid_pass: "(%{num} characters minimum)" + signup_btn: "Sign up" + login_link: "Login" + passwords: + new_form: + title: "Forgot Your Password?" + description: "We get it, stuff happens. Just enter your email address below and we'll send you a link to reset your password!" + reset_btn: "Reset Password" + login_link: "Already have an account? Login!" + edit_form: + title: "Change your password" + description: "Now, You can enter a new password for your account!" + valid_pass: "(%{num} characters minimum)" + change_btn: "Change my password" + login_link: "Already have an account? Login!" activerecord: errors: models: diff --git a/config/locales/manager/en.yml b/config/locales/manager/en.yml index b463df0..9a899ec 100644 --- a/config/locales/manager/en.yml +++ b/config/locales/manager/en.yml @@ -154,6 +154,25 @@ en: create: success: "Create address successfully!" danger: "Create address has problem!" + passwords: + new: + title: "Forgot Your Password?" + description: "We get it, stuff happens. Just enter your email address below and we'll send you a link to reset your password!" + reset_btn: "Reset Password" + login_link: "Already have an account? Login!" + edit: + title: "Change your password" + description: "Now, You can enter a new password for your account!" + valid_pass: "(%{num} characters minimum)" + pass_new: "New password" + pass_confirm: "Confirm new password" + change_btn: "Change my password" + login_link: "Already have an account? Login!" + sessions: + new: + title: Welcome Back! + login_btn: "Log in" + forgot_link: "Forgot your password?" messages: success: admins: diff --git a/config/routes.rb b/config/routes.rb index f079c79..cdd1fe2 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -2,6 +2,7 @@ Rails.application.routes.draw do devise_for :admins, controllers: { sessions: "manager/sessions", passwords: "manager/passwords" } + devise_for :members, controllers: { registrations: "registrations", sessions: "sessions", passwords: "passwords" } namespace :manager do root "members#index" @@ -13,8 +14,9 @@ resources :locations do resources :areas, only: %i[new create] end - resources :areas, except: %i[new create]do + resources :areas, except: %i[new create] do resources :addresses, only: %i[new create] end end + root "home#index" end diff --git a/db/migrate/20190815081049_devise_create_users.rb b/db/migrate/20190815081049_devise_create_users.rb index aae4b5f..58ba95b 100644 --- a/db/migrate/20190815081049_devise_create_users.rb +++ b/db/migrate/20190815081049_devise_create_users.rb @@ -11,6 +11,7 @@ def change t.string :name t.string :address t.string :type + t.string :avatar ## Trackable diff --git a/db/schema.rb b/db/schema.rb index ad42e61..3563848 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -71,12 +71,10 @@ end create_table "prices", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t| - t.bigint "room_id" t.decimal "cost", precision: 8, scale: 2 t.decimal "cleaning_fee", precision: 8, scale: 2, default: "0.0" t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.index ["room_id"], name: "index_prices_on_room_id" end create_table "room_images", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t| @@ -87,15 +85,6 @@ t.index ["room_id"], name: "index_room_images_on_room_id" end - create_table "room_utilities", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t| - t.bigint "room_id" - t.bigint "utility_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["room_id"], name: "index_room_utilities_on_room_id" - t.index ["utility_id"], name: "index_room_utilities_on_utility_id" - end - create_table "rooms", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t| t.bigint "user_id" t.bigint "location_id" @@ -123,6 +112,7 @@ t.string "name" t.string "address" t.string "type" + t.string "avatar" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.boolean "flag", default: false @@ -130,12 +120,6 @@ t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true end - create_table "utilities", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t| - t.string "name" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - end - create_table "vouchers", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t| t.string "code" t.decimal "sale", precision: 10 @@ -147,9 +131,6 @@ add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id" add_foreign_key "addresses", "areas" add_foreign_key "areas", "locations" - add_foreign_key "prices", "rooms" - add_foreign_key "room_utilities", "rooms" - add_foreign_key "room_utilities", "utilities" add_foreign_key "rooms", "locations" add_foreign_key "rooms", "users" end diff --git a/spec/controllers/home_controller_spec.rb b/spec/controllers/home_controller_spec.rb new file mode 100644 index 0000000..99f8bf4 --- /dev/null +++ b/spec/controllers/home_controller_spec.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require "rails_helper" + +RSpec.describe HomeController, type: :controller do + describe "GET #index" do + it "returns http success" do + get :index + expect(response).to have_http_status(:success) + end + end +end diff --git a/spec/views/home/index.html.slim_spec.rb b/spec/views/home/index.html.slim_spec.rb new file mode 100644 index 0000000..fb87fa0 --- /dev/null +++ b/spec/views/home/index.html.slim_spec.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +require "rails_helper" + +RSpec.describe "home/index.html.slim", type: :view do + pending "add some examples to (or delete) #{__FILE__}" +end