diff --git a/.gitignore b/.gitignore index aef65f16f..46baf15fe 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,7 @@ # Ignore master key for decrypting credentials and more. /config/master.key +/config/database.yml /public/packs /public/packs-test diff --git a/Gemfile b/Gemfile index ba5697f15..48313cc45 100644 --- a/Gemfile +++ b/Gemfile @@ -13,8 +13,15 @@ gem 'jbuilder', '~> 2.7' gem 'bootsnap', '>= 1.4.2', require: false gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] +gem 'image_processing' gem 'devise' gem 'hamlit-rails' +gem "aws-sdk-s3", require: false +gem "mini_magick" +gem "font-awesome-rails" +gem "stripe" +gem "hirb" +gem 'redis' group :development, :test do gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] diff --git a/Gemfile.lock b/Gemfile.lock index b425d0d31..7093f7782 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -59,6 +59,22 @@ GEM addressable (2.7.0) public_suffix (>= 2.0.2, < 5.0) ast (2.4.2) + aws-eventstream (1.1.1) + aws-partitions (1.476.0) + aws-sdk-core (3.116.0) + aws-eventstream (~> 1, >= 1.0.2) + aws-partitions (~> 1, >= 1.239.0) + aws-sigv4 (~> 1.1) + jmespath (~> 1.0) + aws-sdk-kms (1.44.0) + aws-sdk-core (~> 3, >= 3.112.0) + aws-sigv4 (~> 1.1) + aws-sdk-s3 (1.96.1) + aws-sdk-core (~> 3, >= 3.112.0) + aws-sdk-kms (~> 1) + aws-sigv4 (~> 1.1) + aws-sigv4 (1.2.4) + aws-eventstream (~> 1, >= 1.0.2) bcrypt (3.1.16) bindex (0.8.1) bootsnap (1.7.3) @@ -84,6 +100,8 @@ GEM factory_bot (~> 6.1.0) railties (>= 5.0.0) ffi (1.15.0) + font-awesome-rails (4.7.0.7) + railties (>= 3.2, < 7) globalid (0.4.2) activesupport (>= 4.2.0) hamlit (2.15.0) @@ -96,10 +114,15 @@ GEM hamlit (>= 1.2.0) railties (>= 4.0.1) hashdiff (1.0.1) + hirb (0.7.3) i18n (1.8.10) concurrent-ruby (~> 1.0) + image_processing (1.12.1) + mini_magick (>= 4.9.5, < 5) + ruby-vips (>= 2.0.17, < 3) jbuilder (2.11.2) activesupport (>= 5.0.0) + jmespath (1.4.0) listen (3.5.1) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) @@ -110,11 +133,14 @@ GEM mini_mime (>= 0.1.1) marcel (1.0.1) method_source (1.0.0) + mini_magick (4.11.0) mini_mime (1.1.0) + mini_portile2 (2.5.3) minitest (5.14.4) msgpack (1.4.2) nio4r (2.5.7) - nokogiri (1.11.3-x86_64-darwin) + nokogiri (1.11.3) + mini_portile2 (~> 2.5.0) racc (~> 1.4) nokogiri (1.11.3-x86_64-linux) racc (~> 1.4) @@ -163,6 +189,7 @@ GEM rb-fsevent (0.10.4) rb-inotify (0.10.1) ffi (~> 1.0) + redis (4.3.1) regexp_parser (2.1.1) responders (3.0.1) actionpack (>= 5.0) @@ -204,6 +231,8 @@ GEM rubocop (~> 1.0) rubocop-ast (>= 1.1.0) ruby-progressbar (1.11.0) + ruby-vips (2.1.2) + ffi (~> 1.12) sass-rails (6.0.0) sassc-rails (~> 2.1, >= 2.1.1) sassc (2.4.0) @@ -227,6 +256,7 @@ GEM actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) + stripe (5.37.0) temple (0.8.2) thor (1.1.0) thread_safe (0.3.6) @@ -259,20 +289,27 @@ GEM zeitwerk (2.4.2) PLATFORMS + ruby x86_64-linux DEPENDENCIES + aws-sdk-s3 bootsnap (>= 1.4.2) byebug database_rewinder devise factory_bot_rails + font-awesome-rails hamlit-rails + hirb + image_processing jbuilder (~> 2.7) listen (~> 3.2) + mini_magick pg puma (~> 4.1) rails (~> 6.0.3, >= 6.0.3.4) + redis rspec-rails rubocop-rails rubocop-rspec @@ -280,6 +317,7 @@ DEPENDENCIES shoulda-matchers spring spring-watcher-listen (~> 2.0.0) + stripe turbolinks (~> 5) tzinfo-data vcr diff --git a/app/assets/images/favicon.ico b/app/assets/images/favicon.ico new file mode 100644 index 000000000..be60b9739 Binary files /dev/null and b/app/assets/images/favicon.ico differ diff --git a/app/assets/images/journey-animate.svg b/app/assets/images/journey-animate.svg new file mode 100644 index 000000000..229ddbde1 --- /dev/null +++ b/app/assets/images/journey-animate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/upload_image.png b/app/assets/images/upload_image.png new file mode 100644 index 000000000..1c65a01aa Binary files /dev/null and b/app/assets/images/upload_image.png differ diff --git a/app/assets/stylesheets/actiontext.scss b/app/assets/stylesheets/actiontext.scss new file mode 100644 index 000000000..7cb26e74a --- /dev/null +++ b/app/assets/stylesheets/actiontext.scss @@ -0,0 +1,36 @@ +// +// Provides a drop-in pointer for the default Trix stylesheet that will format the toolbar and +// the trix-editor content (whether displayed or under editing). Feel free to incorporate this +// inclusion directly in any other asset bundle and remove this file. +// +//= require trix/dist/trix + +// We need to override trix.css’s image gallery styles to accommodate the +// element we wrap around attachments. Otherwise, +// images in galleries will be squished by the max-width: 33%; rule. +.trix-content { + .attachment-gallery { + > action-text-attachment, + > .attachment { + flex: 1 0 33%; + padding: 0 0.5em; + max-width: 33%; + } + + &.attachment-gallery--2, + &.attachment-gallery--4 { + > action-text-attachment, + > .attachment { + flex-basis: 50%; + max-width: 50%; + } + } + } + + action-text-attachment { + .attachment { + padding: 0 !important; + max-width: 100% !important; + } + } +} diff --git a/app/assets/stylesheets/admins.scss b/app/assets/stylesheets/admins.scss new file mode 100644 index 000000000..7d9911c54 --- /dev/null +++ b/app/assets/stylesheets/admins.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the admins controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: https://sass-lang.com/ diff --git a/app/assets/stylesheets/agencies.scss b/app/assets/stylesheets/agencies.scss new file mode 100644 index 000000000..14ec7d6e6 --- /dev/null +++ b/app/assets/stylesheets/agencies.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the agencies controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: https://sass-lang.com/ diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index d05ea0f51..32f1838d3 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -10,6 +10,455 @@ * 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. * + + *= require bootstrap + *= require font-awesome *= require_tree . *= require_self */ + html { + scroll-behavior: smooth; + } + + *{ + font-family: 'Roboto', sans-serif; + margin: 0; + padding: 0; + box-sizing: border-box; + } + +.user-card-full { + overflow: hidden +} + +.card { + border-radius: 5px; + -webkit-box-shadow: 0 1px 20px 0 rgba(69, 90, 100, 0.08); + box-shadow: 0 1px 20px 0 rgba(69, 90, 100, 0.08); + border: none; + margin-bottom: 30px +} + +.user-card-full .user-profile { + border-radius: 5px 0 0 5px +} + +@media only screen and (min-width: 1400px) { + p { + font-size: 14px + } +} + + +#cont{ + width: auto; + height: 500px; +} + +a,#title{ + text-decoration: none +} + +#title{ + font-size: 2.5rem; + font-family: 'Oswald', sans-serif; +} + +#chatTitle{ + font-size: 1.5rem; + font-family: 'Oswald', sans-serif; + text-align: center; + color: white; +} +#chatPrimaryTitle{ + font-size: 1.5rem; + font-family: 'Oswald', sans-serif; + text-align: center; + color: rgb(0, 0, 0); +} + +#chatName{ + font-size: 1rem; + font-family: 'Oswald', sans-serif; + padding-left:10px; + text-decoration: none; +} + +#chatPrimaryName{ + font-size: 1rem; + font-family: 'Oswald', sans-serif; + padding-left:10px; + text-decoration: none; + color:black +} + +#availChats{ + background: linear-gradient(to bottom left, #2b2c2c 0%, #32719b 100%); +} + + +.Package{ + width: 300px; +} + +.Address{ + width: 600px; +} + +#image{ + height:100vh; +} + +#cover{ + height:30vh; + max-width:100%; +} + +#editImage{ + width: 400px; + height:210px; + object-fit: cover +} +/* +#img_prev{ + width: 300px; + height:300px; + object-fit: cover +} */ + +#info { + transition-duration: 0.4s; +} + +#info:hover { + background-color: #4CAF50; /* Green */ + color: white; +} + +#ProfilePic{ + width:150px; + height:150px; + border-radius: 50%; +} + +#form{ + width: 500px; +} + +/* FEATURES */ + + .box { + position: relative; + width: 300px; + border: 1px solid black; + border-radius: 10px; + padding: 90px 15px 15px; + background: linear-gradient(to bottom, rgb(255, 255, 255), rgb(112, 116, 111));; + box-shadow: 0 15px 45px rgba(0,0,0,0.4); + } + + .box:before{ + content: ""; + position: absolute; + border-radius: 10px; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgb(79, 133, 19); + transform: scaleY(0); + transform-origin: top; + transition: transform 0.5s; + } + + .box:hover:before{ + transform: scaleY(1); + transform-origin: bottom; + transition: transform 0.5s; + } + + .box h2{ + position: absolute; + left: 40px; + top: 40px; + font-size: 2.5em; + z-index: 1; + opacity: 0.2; + transition: .5s; + color:rgb(56, 36, 0); + font-family: 'Oswald', sans-serif; + } + + .box:hover h2{ + color:#ffffff; + opacity: 1; + transform: translateY(-20px); + font-family: 'Oswald', sans-serif; + } + + + .box h3{ + position: relative; + font-size: 1.3em; + z-index: 2; + opacity: 0.8; + transition: .5s; + color:rgb(0, 0, 0); + } + + .box:hover h3{ + color:#ffffff; + } + + + .box p{ + position: relative; + z-index: 2; + opacity: 0.8; + transition: .5s; + color:rgb(0, 0, 0); + font-size: 1rem; + } + + .box:hover p{ + color:#ffffff; + } + + + #services{ + font-family: 'PT Sans Narrow', sans-serif; + font-size: 4rem; + } + + .feature{ + background-image: linear-gradient(to bottom, rgba(39, 38, 38, 0), rgb(34, 33, 33)); + } + +/* NAVBAR */ +/* #navbarSupportedContent{ + display: flex; + justify-content: end; + padding-left: 500px; +} + +@media only screen and (max-width: 600px) { + #navbarSupportedContent{ + display: flex; + justify-content: start; + padding-right: 500px; + } +} */ + + +/**/ + +.carousel-content { + position: absolute; + bottom: 20%; + left: 10%; + z-index: 20; + color: white; +} + +.carousel-content h1 { + font-family: 'Oswald', sans-serif; + font-size: 4rem; + -webkit-text-stroke-width: 1px; + -webkit-text-stroke-color: black; +} + +#newImage{ + width: 550px; +} + +#newImage{ + width: 550px; +} + +.carousel-inner{ + background-position: center; + background-repeat: no-repeat; + background-size: cover; + } + +#card{ + height:30vh; + max-width:100%; +} + +#cardBody{ + font-family: 'Roboto Condensed', sans-serif; +} + + +#formId{ + font-size: 1.3rem; + padding-top: 10px; + font-family: 'Roboto Condensed', sans-serif; +} +/* PARALLAX */ + +#parallax { + /* The image used */ + background-image: url("https://images.unsplash.com/photo-1610552050890-fe99536c2615?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1382&q=80"); + + /* Set a specific height */ + min-height: 400px; + + /* Create the parallax scrolling effect */ + background-attachment: fixed; + background-position: center; + background-repeat: no-repeat; + background-size: cover; +} + +#caption{ + font-family: 'PT Sans Narrow', sans-serif; + font-size: 4rem; + color:rgb(0, 0, 0); + text-shadow: 2px 2px #ffffff; +} + + + +/* TEAM SECTION */ + +.our-team-section { + position: relative; + padding-top: 40px; + padding-bottom: 40px; + background-image: linear-gradient(to bottom, rgba(39, 38, 38, 0), rgb(34, 33, 33)); +} + +.our-team-section:before { + position: absolute; + top: -0; + left: 0; + content: " "; + background: url(img/service-section-bottom.png); + background-size: 100% 100px; + width: 100%; + height: 100px; + float: left; + z-index: 99; +} +.our-team { + padding: 30px 0 40px; + background: #f9f9f9; + text-align: center; + overflow: hidden; + position: relative; + border-bottom: 5px solid #00325a; +} +.our-team:hover{ + border-bottom: 5px solid #2f2f2f; +} + +.our-team .pic{ + display: inline-block; + width: 130px; + height: 130px; + margin-bottom: 50px; + z-index: 1; + position: relative; +} +.our-team .pic:before { + content: ""; + width: 100%; + height: 100%; + border-radius: 50%; + background: #00325a; + position: absolute; + bottom: 135%; + right: 0; + left: 0; + opacity: 1; + transform: scale(3); + transition: all 0.3s linear 0s; +} +.our-team:hover .pic:before{ +height: 100%; + background: #2f2f2f; +} +.our-team .pic:after { + content: ""; + width: 100%; + height: 100%; + border-radius: 50%; + background: #ffffff00; + position: absolute; + top: 0; + left: 0; + z-index: 1; + transition: all 0.3s linear 0s; +} +.our-team:hover .pic:after{ + background: #7ab92d; +} +.our-team .pic img { + width: 100%; + height: auto; + border-radius: 50%; + transform: scale(1); + transition: all 0.9s ease 0s; + box-shadow: 0 0 0 14px #f7f5ec; + transform: scale(0.7); + position: relative; + z-index: 2; +} +.our-team:hover .pic img{ + box-shadow: 0 0 0 14px #f7f5ec; + transform: scale(0.7); +} +.our-team .team-content{ margin-bottom: 30px; } +.our-team .title{ + font-size: 22px; + font-weight: 700; + color: #4e5052; + letter-spacing: 1px; + text-transform: capitalize; + margin-bottom: 5px; +} +.our-team .post{ + display: block; + font-size: 15px; + color: #4e5052; + text-transform:capitalize; +} +.our-team .social{ + width: 100%; + padding: 0; + margin: 0; + background: #2f2f2f; + position: absolute; + bottom: -100px; + left: 0; + transition: all 0.5s ease 0s; +} +.our-team:hover .social{ bottom: 0; } +.our-team .social li{ display: inline-block; } +.our-team .social li a{ + display: block; + padding: 10px; + font-size: 17px; + color: #fff; + transition: all 0.3s ease 0s; +} +.our-team .social li a:hover{ + color: #2f2f2f; + background: #f7f5ec; +} +@media only screen and (max-width: 990px){ + .our-team{ margin-bottom: 30px; } + #chatPrimaryName{ + font-size: 0.7rem; + font-family: 'Oswald', sans-serif; + padding-left:10px; + text-decoration: none; + color:black + } +} + +#links{ + color:white; +} diff --git a/app/assets/stylesheets/chat_rooms.scss b/app/assets/stylesheets/chat_rooms.scss new file mode 100644 index 000000000..4e02426fa --- /dev/null +++ b/app/assets/stylesheets/chat_rooms.scss @@ -0,0 +1,56 @@ +// Place all the styles related to the chat_rooms controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: https://sass-lang.com/ + +.chat-room { + min-height: 80vh; + overflow-x: hidden; + .message{ + display:inline-block; + width: 100%; + .content-container{ + width: max-content; + .content{ + background-color: #eee; + padding: 10px 30px 10px 15px; + border-radius: 15px; + } + } + &.me{ + .content-container{ + float: right; + .content{ + background-color: #0d6efd; + color: white; + } + } + } + + } + .author{ + font-size: 0.6rem; + color: #777777; + margin-left: 10px; + } + .chat-box{ + position: absolute; + bottom: 0; + padding-bottom: 20px; + width: calc(100%-15px) + + input{ + height: 45px !important; + font-size: 18px; + padding: 8px; + } + } + .btn{ + height: 45px !important; + } + +} + +.message-container{ + height: 65vh; + overflow-y: scroll; +} \ No newline at end of file diff --git a/app/assets/stylesheets/checkouts.scss b/app/assets/stylesheets/checkouts.scss new file mode 100644 index 000000000..988743b26 --- /dev/null +++ b/app/assets/stylesheets/checkouts.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the checkouts controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: https://sass-lang.com/ diff --git a/app/assets/stylesheets/custom.css.scss b/app/assets/stylesheets/custom.css.scss new file mode 100644 index 000000000..1f21cf8a4 --- /dev/null +++ b/app/assets/stylesheets/custom.css.scss @@ -0,0 +1 @@ +@import 'bootstrap/dist/css/bootstrap'; diff --git a/app/assets/stylesheets/home.scss b/app/assets/stylesheets/home.scss new file mode 100644 index 000000000..072f44eea --- /dev/null +++ b/app/assets/stylesheets/home.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the home controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: https://sass-lang.com/ diff --git a/app/assets/stylesheets/messages.scss b/app/assets/stylesheets/messages.scss new file mode 100644 index 000000000..a3fdc8d23 --- /dev/null +++ b/app/assets/stylesheets/messages.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the messages controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: https://sass-lang.com/ diff --git a/app/assets/stylesheets/tourist_tours.scss b/app/assets/stylesheets/tourist_tours.scss new file mode 100644 index 000000000..986156029 --- /dev/null +++ b/app/assets/stylesheets/tourist_tours.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the tourist_tours controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: https://sass-lang.com/ diff --git a/app/assets/stylesheets/tourists.scss b/app/assets/stylesheets/tourists.scss new file mode 100644 index 000000000..ef4f9ab1b --- /dev/null +++ b/app/assets/stylesheets/tourists.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the tourists controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: https://sass-lang.com/ diff --git a/app/assets/stylesheets/tours.scss b/app/assets/stylesheets/tours.scss new file mode 100644 index 000000000..a7517cb6e --- /dev/null +++ b/app/assets/stylesheets/tours.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the tours controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: https://sass-lang.com/ diff --git a/app/assets/stylesheets/travel_transactions.scss b/app/assets/stylesheets/travel_transactions.scss new file mode 100644 index 000000000..2c55d7199 --- /dev/null +++ b/app/assets/stylesheets/travel_transactions.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the travel_transactions controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: https://sass-lang.com/ diff --git a/app/assets/stylesheets/webhooks.scss b/app/assets/stylesheets/webhooks.scss new file mode 100644 index 000000000..210efb5c2 --- /dev/null +++ b/app/assets/stylesheets/webhooks.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the webhooks controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: https://sass-lang.com/ diff --git a/app/channels/room_channel.rb b/app/channels/room_channel.rb new file mode 100644 index 000000000..b2de416c1 --- /dev/null +++ b/app/channels/room_channel.rb @@ -0,0 +1,9 @@ +class RoomChannel < ApplicationCable::Channel + def subscribed + stream_from "room_channel_#{params[:room_id]}" + end + + def unsubscribed + # Any cleanup needed when channel is unsubscribed + end +end diff --git a/app/controllers/admins_controller.rb b/app/controllers/admins_controller.rb new file mode 100644 index 000000000..c81619d1b --- /dev/null +++ b/app/controllers/admins_controller.rb @@ -0,0 +1,40 @@ +class AdminsController < ApplicationController + before_action :authenticate_admin! + + def index + @users = User.where(type: %w[Tourist Agency], approved: true).all + end + + def show + @admin = Admin.find(params[:id]) + end + + def new + @admin = Admin.new + end + + def create + @admin = Admin.new(admin_params) + if @admin.valid? && @admin.save + redirect_to admins_path, notice: 'Successfully Created Admin!' + else + redirect_back fall_back_location: new_admins_path, alert: @admin.errors_full_messages.first + end + end + + def update + @admin = Admin.find(params[:id]) + @admin.update(admin_params) + if @admin.valid? && @admin.save + redirect_to admin_path(@admin), notice: 'Successfully Created Admin!' + else + redirect_back fall_back_location: edit_admin_path, alert: @admin.errors_full_messages.first + end + end + + private + + def admin_params + params.require(:admin).permit(:email, :first_name, :last_name, :birth_date, :password, :password_confirmation, :cover_pic, :profile_pic) + end +end diff --git a/app/controllers/agencies_controller.rb b/app/controllers/agencies_controller.rb new file mode 100644 index 000000000..542b8673e --- /dev/null +++ b/app/controllers/agencies_controller.rb @@ -0,0 +1,30 @@ +class AgenciesController < ApplicationController + def index + if tourist_signed_in? + @agencies = Agency.where(approved: true) + elsif admin_signed_in? + @agencies = Agency.where(approved: false) + end + end + + def show + @agency = Agency.find(params[:id]) + @tours = @agency.tours + end + + def update + @agency = Agency.find(params[:id]) + if @agency.valid? && @agency.update(agency_params) + @agency.save + redirect_to admins_path, notice: 'Agency approved!' + else + redirect_back fallback_location: agencies_path, alert: @agency.errors.first + end + end + + private + + def agency_params + params.require(:agency).permit(:verified_by, :approved) + end +end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 09705d12a..49179d6ec 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,2 +1,11 @@ class ApplicationController < ActionController::Base + before_action :configure_permitted_parameters, if: :devise_controller? + devise_group :user, contains: %i[tourist agency admin] + + protected + + def configure_permitted_parameters + devise_parameter_sanitizer.permit(:sign_up, keys: %i[type username first_name last_name address birth_date agency_name approved]) + devise_parameter_sanitizer.permit(:account_update, keys: %i[first_name last_name profile_pic cover_pic address birth_date agency_name approved]) + end end diff --git a/app/controllers/chat_rooms_controller.rb b/app/controllers/chat_rooms_controller.rb new file mode 100644 index 000000000..ed6b42ce1 --- /dev/null +++ b/app/controllers/chat_rooms_controller.rb @@ -0,0 +1,26 @@ +class ChatRoomsController < ApplicationController + before_action :authenticate_user! + before_action :set_room, only: %i[show] + def chat_user + user = User.find(params[:user_id]) + ChatRoom.create(users: [current_user, user]) unless current_user.chat_room(user) + redirect_to chat_rooms_path + end + + def show + @rooms = current_user.chat_rooms + render 'index' + end + + def index + @rooms = current_user.chat_rooms + end + + private + + # Use callbacks to share common setup or constraints between actions. + def set_room + @room = current_user.chat_rooms.find_by(id: params[:id]) + redirect_to chat_rooms_path unless @room + end +end diff --git a/app/controllers/checkouts_controller.rb b/app/controllers/checkouts_controller.rb new file mode 100644 index 000000000..f7813c8a0 --- /dev/null +++ b/app/controllers/checkouts_controller.rb @@ -0,0 +1,33 @@ +class CheckoutsController < ApplicationController + def create + Stripe.api_key = Rails.application.credentials.stripe[:secret_key] + @tour = Tour.find(params[:tour_id]) + @session = Stripe::Checkout::Session.create({ + payment_method_types: ['card'], + line_items: [{ + name: @tour.name, + amount: (@tour.price * 100).to_i, + currency: 'usd', + quantity: params[:quantity] + }], + payment_intent_data: { + metadata: { + tourist_id: current_tourist.id, + tour_id: @tour.id, + price: @tour.price, + start_date: params[:start_date], + end_date: params[:end_date], + guest_quantity: params[:quantity], + total_amount: params[:amount] + } + }, + mode: 'payment', + success_url: root_url, + cancel_url: new_tourist_tour_url(@tour) + }) + + respond_to do |format| + format.js + end + end +end diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb new file mode 100644 index 000000000..e5661cc07 --- /dev/null +++ b/app/controllers/home_controller.rb @@ -0,0 +1,11 @@ +class HomeController < ApplicationController + # before_action :authenticate_user! + # before_action :redirect + def index; end + + private + + def redirect + redirect_to tours_path, notice: 'Succesfully Signed-in!' if agency_signed_in? + end +end diff --git a/app/controllers/messages_controller.rb b/app/controllers/messages_controller.rb new file mode 100644 index 000000000..a53cb8994 --- /dev/null +++ b/app/controllers/messages_controller.rb @@ -0,0 +1,19 @@ +class MessagesController < ApplicationController + def create + @message = Message.new(message_params) + @message.user = current_user + @message.save + mine = ApplicationController.render(partial: 'messages/mine', locals: { message: @message }) + theirs = ApplicationController.render(partial: 'messages/theirs', locals: { message: @message }) + message = @message + ActionCable.server.broadcast "room_channel_#{message.chat_room_id}", { mine: mine, theirs: theirs, message: message } + end + + private + + # Use callbacks to share common setup or constraints between actions. + # Only allow a list of trusted parameters through. + def message_params + params.require(:message).permit(:body, :user_id, :chat_room_id) + end +end diff --git a/app/controllers/tourist_tours_controller.rb b/app/controllers/tourist_tours_controller.rb new file mode 100644 index 000000000..1e4fa439a --- /dev/null +++ b/app/controllers/tourist_tours_controller.rb @@ -0,0 +1,33 @@ +class TouristToursController < ApplicationController + before_action :authenticate_tourist! + def index + @tourist_tours = current_tourist.tourist_tours.all + end + + def show + @tourist_tour = current_tourist.tourist_tours.find(params[:id]) + end + + def new + @tour = Tour.find(params[:tour_id]) + @tourist_tour = current_tourist.tourist_tours.build + end + + def create + @tourist_tour = current_tourist.tourist_tours.build(tourist_tour_params) + @tourist_tour.save + if @tourist_tour.save + TravelTransaction.create(tourist_tour_id: @tourist_tour.id, agency_id: @tourist_tour.tour.agency.id, total_price: @tourist_tour.amount_bought) + redirect_to tourist_tours_path, notice: "Successfully bought #{@tourist_tour.tour.name} package!" + + else + redirect_to tours_path, alert: @tourist_tour.errors.full_messages.first + end + end + + private + + def tourist_tour_params + params.require(:tourist_tour).permit(:tour_id, :guest_quantity, :amount_bought, :start_date, :end_date) + end +end diff --git a/app/controllers/tourists_controller.rb b/app/controllers/tourists_controller.rb new file mode 100644 index 000000000..ec207fc82 --- /dev/null +++ b/app/controllers/tourists_controller.rb @@ -0,0 +1,7 @@ +class TouristsController < ApplicationController + before_action :authenticate_user! + + def show + @tourist = Tourist.find(params[:id]) + end +end diff --git a/app/controllers/tours_controller.rb b/app/controllers/tours_controller.rb new file mode 100644 index 000000000..fbf53066d --- /dev/null +++ b/app/controllers/tours_controller.rb @@ -0,0 +1,49 @@ +class ToursController < ApplicationController + before_action :authenticate_agency!, except: %i[index show] + + def index + if tourist_signed_in? + @tours = Tour.all + elsif agency_signed_in? + @tours = current_agency.tours.all + end + end + + def show + @tour = Tour.find(params[:id]) + end + + def new + @tour = current_agency.tours.build + end + + def create + @tour = current_agency.tours.build(tour_params) + if @tour.valid? + @tour.save + redirect_to tours_path, notice: "Successfully created #{@tour.name} package" + else + redirect_back fallback_location: new_tour_path, alert: @tour.errors.full_messages.first + end + end + + def edit + @tour = Tour.find(params[:id]) + end + + def update + @tour = Tour.find(params[:id]) + if @tour.update(tour_params) + flash[:notice] = 'tour was updated successfully.' + redirect_to tour_path(@tour) + else + render 'edit' + end + end + + private + + def tour_params + params.require(:tour).permit(:name, :price, :location, :duration, :details, images: []) + end +end diff --git a/app/controllers/travel_transactions_controller.rb b/app/controllers/travel_transactions_controller.rb new file mode 100644 index 000000000..a91e5cb55 --- /dev/null +++ b/app/controllers/travel_transactions_controller.rb @@ -0,0 +1,11 @@ +class TravelTransactionsController < ApplicationController + before_action :authenticate_user! + + def index + if tourist_signed_in? || agency_signed_in? + @travel_transactions = current_user.travel_transactions + elsif admin_signed_in? + @travel_transactions = TravelTransaction.all + end + end +end diff --git a/app/controllers/webhooks_controller.rb b/app/controllers/webhooks_controller.rb new file mode 100644 index 000000000..e54d03fa6 --- /dev/null +++ b/app/controllers/webhooks_controller.rb @@ -0,0 +1,51 @@ +class WebhooksController < ApplicationController + skip_before_action :verify_authenticity_token + + def create + payload = request.body.read + sig_header = request.env['HTTP_STRIPE_SIGNATURE'] + event = nil + + begin + event = Stripe::Webhook.construct_event( + payload, sig_header, Rails.application.credentials[:stripe][:webhook] + ) + rescue JSON::ParserError => e + logger.error(e) + status 400 + return + rescue Stripe::SignatureVerificationError => e + # Invalid signature + logger = Logger.new($stdout) + logger.error('Signature error') + logger.error(e) + return + end + + # Handle the event + case event.type + when 'checkout.session.completed' + logger = Logger.new($stdout) + session = event.data.object + payment_intent = session.payment_intent + stripe = Stripe::PaymentIntent.retrieve(payment_intent) + data = stripe.metadata + + logger.error("This is the data #{data}") + @tourist_tour = TouristTour.create( + tour_id: data.tour_id, + tourist_id: data.tourist_id, + amount_bought: data.total_amount, + guest_quantity: data.guest_quantity, + start_date: data.start_date, + end_date: data.end_date + ) + tour = Tour.find(data.tour_id) + @travel_transaction = TravelTransaction.create(tourist_tour: @tourist_tour, + agency_id: tour.agency.id, + total_price: data.total_amount) + end + + render json: { message: 'success' } + end +end diff --git a/app/helpers/admins_helper.rb b/app/helpers/admins_helper.rb new file mode 100644 index 000000000..d4f7b3486 --- /dev/null +++ b/app/helpers/admins_helper.rb @@ -0,0 +1,2 @@ +module AdminsHelper +end diff --git a/app/helpers/agencies_helper.rb b/app/helpers/agencies_helper.rb new file mode 100644 index 000000000..9d3751428 --- /dev/null +++ b/app/helpers/agencies_helper.rb @@ -0,0 +1,2 @@ +module AgenciesHelper +end diff --git a/app/helpers/chat_rooms_helper.rb b/app/helpers/chat_rooms_helper.rb new file mode 100644 index 000000000..4d46c38db --- /dev/null +++ b/app/helpers/chat_rooms_helper.rb @@ -0,0 +1,2 @@ +module ChatRoomsHelper +end diff --git a/app/helpers/checkouts_helper.rb b/app/helpers/checkouts_helper.rb new file mode 100644 index 000000000..d6ebab7ab --- /dev/null +++ b/app/helpers/checkouts_helper.rb @@ -0,0 +1,2 @@ +module CheckoutsHelper +end diff --git a/app/helpers/home_helper.rb b/app/helpers/home_helper.rb new file mode 100644 index 000000000..23de56ac6 --- /dev/null +++ b/app/helpers/home_helper.rb @@ -0,0 +1,2 @@ +module HomeHelper +end diff --git a/app/helpers/messages_helper.rb b/app/helpers/messages_helper.rb new file mode 100644 index 000000000..f1bca9f6c --- /dev/null +++ b/app/helpers/messages_helper.rb @@ -0,0 +1,2 @@ +module MessagesHelper +end diff --git a/app/helpers/tourist_tours_helper.rb b/app/helpers/tourist_tours_helper.rb new file mode 100644 index 000000000..754c2fa73 --- /dev/null +++ b/app/helpers/tourist_tours_helper.rb @@ -0,0 +1,2 @@ +module TouristToursHelper +end diff --git a/app/helpers/tourists_helper.rb b/app/helpers/tourists_helper.rb new file mode 100644 index 000000000..f70b4ffb5 --- /dev/null +++ b/app/helpers/tourists_helper.rb @@ -0,0 +1,2 @@ +module TouristsHelper +end diff --git a/app/helpers/tours_helper.rb b/app/helpers/tours_helper.rb new file mode 100644 index 000000000..b430fec50 --- /dev/null +++ b/app/helpers/tours_helper.rb @@ -0,0 +1,2 @@ +module ToursHelper +end diff --git a/app/helpers/travel_transactions_helper.rb b/app/helpers/travel_transactions_helper.rb new file mode 100644 index 000000000..53d4c1519 --- /dev/null +++ b/app/helpers/travel_transactions_helper.rb @@ -0,0 +1,2 @@ +module TravelTransactionsHelper +end diff --git a/app/helpers/webhooks_helper.rb b/app/helpers/webhooks_helper.rb new file mode 100644 index 000000000..3fa665674 --- /dev/null +++ b/app/helpers/webhooks_helper.rb @@ -0,0 +1,2 @@ +module WebhooksHelper +end diff --git a/app/javascript/channels/room_channel.js b/app/javascript/channels/room_channel.js new file mode 100644 index 000000000..e7804e12d --- /dev/null +++ b/app/javascript/channels/room_channel.js @@ -0,0 +1,43 @@ +import consumer from "./consumer" + +const scroll_bottom = function () { + if ($('.message-container').length > 0) { + $('.message-container').scrollTop($('.message-container')[0].scrollHeight) + } +} +const clear_input = function () { + $('#message_body').val('') +} +document.addEventListener('turbolinks:load', () => { + const room_id = $('#room-id').attr('data-room-id'); + consumer.subscriptions.create({ channel: "RoomChannel", room_id: room_id }, { + connected() { + console.log(`Connected to Channel ${room_id}`) + // Called when the subscription is ready for use on the server + }, + + disconnected() { + // Called when the subscription has been terminated by the server + }, + + received(data) { + console.log(room_id) + const user_id = $('#user-id').attr('data-user-id'); + let html; + console.log(user_id); + console.log(data.message.user_id); + if (user_id == data.message.user_id) { + html = data.mine + } + else{ + html = data.theirs + } + if (room_id == data.message.chat_room_id) { + $(`#message-container-${data.message.chat_room_id}`).append(html); + scroll_bottom(); + clear_input(); + } + + } + }); +}) \ No newline at end of file diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js index 9cd55d4b9..1f923b299 100644 --- a/app/javascript/packs/application.js +++ b/app/javascript/packs/application.js @@ -15,3 +15,26 @@ require("channels") // // const images = require.context('../images', true) // const imagePath = (name) => images(name, true) + +import "bootstrap" + +require("trix") +require("@rails/actiontext") + + + +function hideFlash() { + setInterval(function() {document.querySelector('#flash').style.display = "none";}, 3000); +} + +hideFlash(); + +const scroll_bottom = function () { + if ($('.message-container').length > 0) { + $('.message-container').scrollTop($('.message-container')[0].scrollHeight) + } +} +$(document).on('turbolinks:load', function() { + scroll_bottom(); +}) + diff --git a/app/javascript/packs/image_preview.js b/app/javascript/packs/image_preview.js new file mode 100644 index 000000000..4a4224393 --- /dev/null +++ b/app/javascript/packs/image_preview.js @@ -0,0 +1,34 @@ +$(function () { + const output = document.getElementById('img_prev'); + // Multiple images preview in browser + var imagesPreview = function(input, placeToInsertImagePreview) { + $(placeToInsertImagePreview).empty() + if (input.files) { + var filesAmount = input.files.length; + + for (i = 0; i < filesAmount; i++) { + var reader = new FileReader(); + + reader.onload = function(event) { + $($.parseHTML('')).attr('src', event.target.result).addClass('tour_img').appendTo(placeToInsertImagePreview); + } + + reader.readAsDataURL(input.files[i]); + } + } + + }; + + $('#tour_images').on('change', function() { + imagesPreview(this, 'div.images-container'); + }); + $('#images-prev').on("click", function (event) { + $('img.active').removeClass('active') + $(event.target).closest("img").toggleClass("active"); + console.log(output); + output.src = $(event.target).closest("img").attr('src') + }); + + + +}); \ No newline at end of file diff --git a/app/javascript/packs/tourist_tour_new.js b/app/javascript/packs/tourist_tour_new.js new file mode 100644 index 000000000..830cc4e0a --- /dev/null +++ b/app/javascript/packs/tourist_tour_new.js @@ -0,0 +1,22 @@ +const { start } = require("@popperjs/core") + +// For Guest quantity and Total price +$("#guest_quantity").on("input", function(){ + $("#total_price").val($("#guest_quantity").val() * Number($("#tour-price").attr('data-price'))) +}) + + +// For Date and Duration + +$("#start-date").on("input", function(){ + let duration = Number($("#tour-duration").attr('data-duration')) + let start_date = new Date($("#start-date").val()) + console.log(duration) + console.log($("#start-date").val()) + let end_date = new Date(start_date.setDate(new Date($("#start-date").val()).getDate() + duration)) + let new_month = `${end_date.getMonth()+1 < 9 ? '0' : ''}${end_date.getMonth() + 1}` + let new_day = `${end_date.getDate() < 9 ? '0': ''}${end_date.getDate()}` + + $("#end-date").val(`${end_date.getFullYear()}-${new_month}-${new_day}`) + console.log(`${end_date.getFullYear()}-${new_month}-${new_day}`) +}) \ No newline at end of file diff --git a/app/models/admin.rb b/app/models/admin.rb new file mode 100644 index 000000000..9bba62b29 --- /dev/null +++ b/app/models/admin.rb @@ -0,0 +1,7 @@ +class Admin < User + after_create :set_approved_to_true + + def set_approved_to_true + update(approved: true) + end +end diff --git a/app/models/agency.rb b/app/models/agency.rb new file mode 100644 index 000000000..6fe728d6f --- /dev/null +++ b/app/models/agency.rb @@ -0,0 +1,8 @@ +class Agency < User + has_many :tours, dependent: :nullify + has_many :travel_transactions, dependent: :nullify + + has_many :reviews, dependent: :nullify + has_many :chat_room_users, dependent: :nullify, foreign_key: 'user_id', inverse_of: :user + has_many :chat_rooms, through: :chat_room_users +end diff --git a/app/models/chat_room.rb b/app/models/chat_room.rb new file mode 100644 index 000000000..e0be869c5 --- /dev/null +++ b/app/models/chat_room.rb @@ -0,0 +1,9 @@ +class ChatRoom < ApplicationRecord + has_many :messages, dependent: :nullify + has_many :chat_room_users, dependent: :nullify + has_many :users, through: :chat_room_users + + def chat_mate(current_user) + users.reject { |user| user == current_user }.first + end +end diff --git a/app/models/chat_room_user.rb b/app/models/chat_room_user.rb new file mode 100644 index 000000000..b3dac6b32 --- /dev/null +++ b/app/models/chat_room_user.rb @@ -0,0 +1,4 @@ +class ChatRoomUser < ApplicationRecord + belongs_to :user, class_name: 'User' + belongs_to :chat_room +end diff --git a/app/models/message.rb b/app/models/message.rb new file mode 100644 index 000000000..eda9ddec7 --- /dev/null +++ b/app/models/message.rb @@ -0,0 +1,4 @@ +class Message < ApplicationRecord + belongs_to :chat_room + belongs_to :user +end diff --git a/app/models/review.rb b/app/models/review.rb new file mode 100644 index 000000000..21d3cbcd1 --- /dev/null +++ b/app/models/review.rb @@ -0,0 +1,6 @@ +class Review < ApplicationRecord + validates :rating, presence: true, numericality: { greater_than_or_equal_to: 0 } + + belongs_to :tourist + belongs_to :agency +end diff --git a/app/models/tour.rb b/app/models/tour.rb new file mode 100644 index 000000000..f61f431e9 --- /dev/null +++ b/app/models/tour.rb @@ -0,0 +1,14 @@ +class Tour < ApplicationRecord + validates :price, presence: true, numericality: { greater_than_or_equal_to: 0 } + validates :name, presence: true, length: { minimum: 1 } + validates :location, presence: true, length: { minimum: 1 } + validates :duration, presence: true, numericality: { greater_than_or_equal_to: 1 } + + belongs_to :agency + has_many :tourist_tours, dependent: :nullify + has_many :tourists, through: :tourist_tours + + has_many :travel_transactions, through: :tourist_tours + has_rich_text :details + has_many_attached :images +end diff --git a/app/models/tourist.rb b/app/models/tourist.rb new file mode 100644 index 000000000..def35e7e0 --- /dev/null +++ b/app/models/tourist.rb @@ -0,0 +1,15 @@ +class Tourist < User + has_many :tourist_tours, dependent: :nullify + has_many :tours, through: :tourist_tours + + has_many :travel_transactions, through: :tourist_tours + has_many :reviews, dependent: :nullify + has_many :chat_room_users, dependent: :nullify, foreign_key: 'user_id', inverse_of: :user + has_many :chat_rooms, through: :chat_room_users + + after_create :set_approved_to_true + + def set_approved_to_true + update(approved: true) + end +end diff --git a/app/models/tourist_tour.rb b/app/models/tourist_tour.rb new file mode 100644 index 000000000..bcea3f2c1 --- /dev/null +++ b/app/models/tourist_tour.rb @@ -0,0 +1,9 @@ +class TouristTour < ApplicationRecord + validates :guest_quantity, presence: true, numericality: { greater_than_or_equal_to: 1 } + validates :amount_bought, presence: true, numericality: { greater_than_or_equal_to: 0 } + + belongs_to :tour + belongs_to :tourist + + has_one :travel_transaction, dependent: :nullify +end diff --git a/app/models/travel_transaction.rb b/app/models/travel_transaction.rb new file mode 100644 index 000000000..a707a6365 --- /dev/null +++ b/app/models/travel_transaction.rb @@ -0,0 +1,6 @@ +class TravelTransaction < ApplicationRecord + validates :total_price, presence: true, numericality: { greater_than_or_equal_to: 1 } + + belongs_to :tourist_tour + belongs_to :agency +end diff --git a/app/models/user.rb b/app/models/user.rb new file mode 100644 index 000000000..257f3fc2f --- /dev/null +++ b/app/models/user.rb @@ -0,0 +1,42 @@ +class User < ApplicationRecord + # Include default devise modules. Others available are: + # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable + devise :database_authenticatable, :registerable, + :recoverable, :rememberable, :validatable + + has_one_attached :profile_pic + has_one_attached :cover_pic + has_many :messages, dependent: :nullify + + def chat_room(user) + chat_rooms.select { |chat_room| chat_room.users.include?(user) }.first + end + + def tourist? + type == 'Tourist' + end + + def agency? + type == 'Agency' + end + + def admin? + type == 'Admin' + end + + def username + if agency? + agency_name + else + "#{first_name} #{last_name}" + end + end + + def active_for_authentication? + super && approved + end + + def inactive_message + approved ? super : :not_approved + end +end diff --git a/app/views/active_storage/blobs/_blob.html.erb b/app/views/active_storage/blobs/_blob.html.erb new file mode 100644 index 000000000..49ba357dd --- /dev/null +++ b/app/views/active_storage/blobs/_blob.html.erb @@ -0,0 +1,14 @@ +
attachment--<%= blob.filename.extension %>"> + <% if blob.representable? %> + <%= image_tag blob.representation(resize_to_limit: local_assigns[:in_gallery] ? [ 800, 600 ] : [ 1024, 768 ]) %> + <% end %> + +
+ <% if caption = blob.try(:caption) %> + <%= caption %> + <% else %> + <%= blob.filename %> + <%= number_to_human_size blob.byte_size %> + <% end %> +
+
diff --git a/app/views/admins/edit.html.erb b/app/views/admins/edit.html.erb new file mode 100644 index 000000000..1cf0fae61 --- /dev/null +++ b/app/views/admins/edit.html.erb @@ -0,0 +1,81 @@ +<%= form_for(resource, as: resource_name, url: edit_admin_path, html: { method: :put }) do |f| %> +<%= render "devise/shared/error_messages", resource: resource %> + +
+
+
+

Edit <%= resource_name.to_s.humanize %>

+
+
+ <%= f.label :email %>
+ <%= f.email_field :email, autofocus: true, autocomplete: "email",placeholder:"Email",class:"form-control" %> +
+
+ <%= f.label :first_name%>
+ <%= f.text_field :first_name, class:"form-control"%> +
+
+ <%= f.label :last_name%>
+ <%= f.text_field :last_name, class:"form-control"%> +
+
+ <%= f.label :birth_date%>
+ <%= f.date_field :birth_date, class:"form-control"%> +
+
+ <%= f.label :address%>
+ <%= f.text_field :address, class:"form-control"%> +
+ +
+ <%= f.label :password %>
+ <% if @minimum_password_length %> + (<%= @minimum_password_length %> characters minimum) + <% end %>
+ <%= f.password_field :password, autocomplete: "new-password",placeholder:"Password",class:"form-control" %> +
+
+ <%= f.label :password_confirmation %>
+ <%= f.password_field :password_confirmation, autocomplete: "new-password",placeholder:"Confirm Password",class:"form-control" %> +
+
+ <%= f.label :current_password %>
+ <%= f.password_field :current_password, autocomplete: "current-password",placeholder:"Current Password",class:"form-control" %> +
+
+
+
+
+
+ <%= f.label "Cover Photo"%> + <%= f.file_field :cover_pic%> +
+ <%if current_user.cover_pic.attached?%> + + <%else%> + + <%end%> +
+ <%= f.label "Profile Photo"%> + <%= f.file_field :profile_pic%> +
+ <%if current_user.profile_pic.attached?%> + + <%else%> + + <%end%> +
+
+
+
+
+
+ <%= f.submit "Update",class:"btn btn-success w-80 font-weight-bold mt-4 mx-2" %> +
+
+<% end %> +
+
+ <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete, class:"btn btn-danger w-80" %>

+
+
diff --git a/app/views/admins/index.html.erb b/app/views/admins/index.html.erb new file mode 100644 index 000000000..32acca8aa --- /dev/null +++ b/app/views/admins/index.html.erb @@ -0,0 +1,25 @@ +



+ + + + + + + + + + + <% @users.each do |user|%> + + <% if user.type == 'Tourist' %> + + <%elsif user.type == 'Agency'%> + + <% end %> + + + + + <%end%> + +
UserEmailTypeAddress
<%= link_to "#{ user.first_name} #{ user.last_name}", tourist_path(user)%><%= link_to "#{ user.agency_name}", agency_path(user)%><%= user.type%><%= user.email%><%= user.address%>
\ No newline at end of file diff --git a/app/views/admins/new.html.erb b/app/views/admins/new.html.erb new file mode 100644 index 000000000..46d845daf --- /dev/null +++ b/app/views/admins/new.html.erb @@ -0,0 +1,46 @@ + +<%= render 'shared/errors', obj: @admin %> +
+
+
+

Create Admin

+
+ <%= form_with scope: :admin, model: @admin, local: true do |f| %> +
+ <%= f.label :email %>
+ <%= f.email_field :email, autofocus: true, autocomplete: "email",class:"form-control" %> +
+
+ <%= f.label :first_name%>
+ <%= f.text_field :first_name, class:"form-control"%> +
+
+ <%= f.label :last_name%>
+ <%= f.text_field :last_name, class:"form-control"%> +
+
+ <%= f.label :birth_date%>
+ <%= f.date_field :birth_date, class:"form-control"%> +
+ +
+ <%= f.label :password %>
+ <% if @minimum_password_length %> + (<%= @minimum_password_length %> characters minimum) + <% end %>
+ <%= f.password_field :password, autocomplete: "new-password",placeholder:"Password",class:"form-control" %> +
+
+ <%= f.label :password_confirmation %>
+ <%= f.password_field :password_confirmation, autocomplete: "new-password",placeholder:"Confirm Password",class:"form-control" %> +
+
+
+ <%= f.submit "Sign up",class:"btn btn-dark w-100 font-weight-bold mt-2" %> +
+
+ <% end %> +
+
+
+
\ No newline at end of file diff --git a/app/views/admins/show.html.erb b/app/views/admins/show.html.erb new file mode 100644 index 000000000..7fa2dffd4 --- /dev/null +++ b/app/views/admins/show.html.erb @@ -0,0 +1,84 @@ +



+
+
+
+ <%if @admin.cover_pic.attached?%> + + <%else%> + + <%end%> +

+
+
+
+ <%if @admin.profile_pic.attached?%> + + <%else%> + + <%end%> +
+

<%= @admin.first_name%> <%=@admin.last_name%>

+

Admin

+ <% if @admin == current_admin%> + <%= button_to 'Edit Profile', edit_admin_path,class:"btn btn-info ", method: :get%> + <% end %> +
+
+
+
+
+
+

+
+
+
+
First Name
+
+
+ <%= @admin.first_name%> +
+
+
+
+
+
Last Name
+
+
+ <%=@admin.last_name%> +
+
+
+
+
+
Email
+
+
+ <%= @admin.email%> +
+
+
+
+
+
Birth Date
+
+
+ <%= @admin&.birth_date&.strftime('%b %d, %Y')%> +
+
+
+
+
+
Address
+
+
+ <%= @admin.address%> +
+
+
+ +
+
+
+
+
+
\ No newline at end of file diff --git a/app/views/agencies/index.html.erb b/app/views/agencies/index.html.erb new file mode 100644 index 000000000..18419ea32 --- /dev/null +++ b/app/views/agencies/index.html.erb @@ -0,0 +1,26 @@ +




+ + + + + + + <% if admin_signed_in? %> + + <% end %> + + + + + <% @agencies.each do |agency|%> + + + + + <% if admin_signed_in? %> + + <% end %> + + <%end%> + +
AgencyAgency EmailAddressAction
<%= link_to agency.agency_name, agency_path(agency)%><%= agency.email%><%= agency.address%><%= button_to 'Approve', agency_path(agency), params: {agency: { approved: true, verified_by: current_admin.id }}, method: :patch, local: true%>
\ No newline at end of file diff --git a/app/views/agencies/show.html.erb b/app/views/agencies/show.html.erb new file mode 100644 index 000000000..3452aaf50 --- /dev/null +++ b/app/views/agencies/show.html.erb @@ -0,0 +1,93 @@ +

+
+
+
+ <%if @agency.cover_pic.attached?%> + + <%else%> + + <%end%> +

+
+
+
+ <%if @agency.profile_pic.attached?%> + + <%else%> + + <%end%> +
+

<%= @agency.agency_name%>

+

Agency

+ <% if agency_signed_in?%> + <%= button_to 'Edit Profile', edit_agency_registration_path,class:"btn btn-info ", method: :get%> + <% end %> +
+
+
+
+
+
+

+
+
+
+
Name
+
+
+ <%= @agency.agency_name%> +
+
+
+
+
+
Email
+
+
+ <%= @agency.email%> +
+
+
+
+
+
Address
+
+
+ <%= @agency.address%> +
+
+
+
+
+
Packages Created
+
+
+ <%= @agency.tours.count%> +
+
+
+
+
+
Rating
+
+
+ <%#= @agency.address%> Very Good +
+
+ <% if @agency.verified_by %> +
+
+
+
Verified By:
+
+
+ <%= Admin.find(@agency.verified_by).email%> +
+
+ <% end %> +
+
+
+
+
+
diff --git a/app/views/chat_rooms/index.html.erb b/app/views/chat_rooms/index.html.erb new file mode 100644 index 000000000..9d433236a --- /dev/null +++ b/app/views/chat_rooms/index.html.erb @@ -0,0 +1,42 @@ +



+
>
+
>
+
+
+
+

Hello, <%=current_user&.first_name%>

+ <% @rooms.each do |room| %> + <%= link_to room do%> +
+
+ <%if room.chat_mate(current_user).profile_pic.attached?%> + + <%end%> +

<%=room.chat_mate(current_user).username%>

+
+
+ <%end%> + <%end%> +
+
+ <% if @room.present?%> +
+ +
+ <% @room.messages.each do |message|%> + <%=render 'messages/message', message: message%> + <%end%> +
+
+ <%= render 'messages/form', message: Message.new, room: @room %> +
+
+ <%end%> +
+
\ No newline at end of file diff --git a/app/views/checkouts/create.js.erb b/app/views/checkouts/create.js.erb new file mode 100644 index 000000000..437c631b2 --- /dev/null +++ b/app/views/checkouts/create.js.erb @@ -0,0 +1,7 @@ +let stripe = Stripe("pk_test_51JFGdtFoETpJwnmpLYHGKELRei5AuHWiNv3ElBWKokItV3RYnl7B3mXbiyed6weJLsJtmgXqr73tPIYb2XVdBKqy00SNPnJujx"); +let checkoutButton = document.getElementById('checkout-button'); +stripe.redirectToCheckout({ + sessionId: '<%= @session.id %>', +}).then(function (result) { + +}); \ No newline at end of file diff --git a/app/views/devise/confirmations/new.html.erb b/app/views/devise/confirmations/new.html.erb new file mode 100644 index 000000000..b12dd0cbe --- /dev/null +++ b/app/views/devise/confirmations/new.html.erb @@ -0,0 +1,16 @@ +

Resend confirmation instructions

+ +<%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %> + <%= render "devise/shared/error_messages", resource: resource %> + +
+ <%= f.label :email %>
+ <%= f.email_field :email, autofocus: true, autocomplete: "email", value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email) %> +
+ +
+ <%= f.submit "Resend confirmation instructions" %> +
+<% end %> + +<%= render "devise/shared/links" %> diff --git a/app/views/devise/mailer/confirmation_instructions.html.erb b/app/views/devise/mailer/confirmation_instructions.html.erb new file mode 100644 index 000000000..dc55f64f6 --- /dev/null +++ b/app/views/devise/mailer/confirmation_instructions.html.erb @@ -0,0 +1,5 @@ +

Welcome <%= @email %>!

+ +

You can confirm your account email through the link below:

+ +

<%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %>

diff --git a/app/views/devise/mailer/email_changed.html.erb b/app/views/devise/mailer/email_changed.html.erb new file mode 100644 index 000000000..32f4ba803 --- /dev/null +++ b/app/views/devise/mailer/email_changed.html.erb @@ -0,0 +1,7 @@ +

Hello <%= @email %>!

+ +<% if @resource.try(:unconfirmed_email?) %> +

We're contacting you to notify you that your email is being changed to <%= @resource.unconfirmed_email %>.

+<% else %> +

We're contacting you to notify you that your email has been changed to <%= @resource.email %>.

+<% end %> diff --git a/app/views/devise/mailer/password_change.html.erb b/app/views/devise/mailer/password_change.html.erb new file mode 100644 index 000000000..b41daf476 --- /dev/null +++ b/app/views/devise/mailer/password_change.html.erb @@ -0,0 +1,3 @@ +

Hello <%= @resource.email %>!

+ +

We're contacting you to notify you that your password has been changed.

diff --git a/app/views/devise/mailer/reset_password_instructions.html.erb b/app/views/devise/mailer/reset_password_instructions.html.erb new file mode 100644 index 000000000..f667dc12f --- /dev/null +++ b/app/views/devise/mailer/reset_password_instructions.html.erb @@ -0,0 +1,8 @@ +

Hello <%= @resource.email %>!

+ +

Someone has requested a link to change your password. You can do this through the link below.

+ +

<%= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token) %>

+ +

If you didn't request this, please ignore this email.

+

Your password won't change until you access the link above and create a new one.

diff --git a/app/views/devise/mailer/unlock_instructions.html.erb b/app/views/devise/mailer/unlock_instructions.html.erb new file mode 100644 index 000000000..41e148bf2 --- /dev/null +++ b/app/views/devise/mailer/unlock_instructions.html.erb @@ -0,0 +1,7 @@ +

Hello <%= @resource.email %>!

+ +

Your account has been locked due to an excessive number of unsuccessful sign in attempts.

+ +

Click the link below to unlock your account:

+ +

<%= link_to 'Unlock my account', unlock_url(@resource, unlock_token: @token) %>

diff --git a/app/views/devise/passwords/edit.html.erb b/app/views/devise/passwords/edit.html.erb new file mode 100644 index 000000000..5fbb9ff0a --- /dev/null +++ b/app/views/devise/passwords/edit.html.erb @@ -0,0 +1,25 @@ +

Change your password

+ +<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| %> + <%= render "devise/shared/error_messages", resource: resource %> + <%= f.hidden_field :reset_password_token %> + +
+ <%= f.label :password, "New password" %>
+ <% if @minimum_password_length %> + (<%= @minimum_password_length %> characters minimum)
+ <% end %> + <%= f.password_field :password, autofocus: true, autocomplete: "new-password" %> +
+ +
+ <%= f.label :password_confirmation, "Confirm new password" %>
+ <%= f.password_field :password_confirmation, autocomplete: "new-password" %> +
+ +
+ <%= f.submit "Change my password" %> +
+<% end %> + +<%= render "devise/shared/links" %> diff --git a/app/views/devise/passwords/new.html.erb b/app/views/devise/passwords/new.html.erb new file mode 100644 index 000000000..9b486b81b --- /dev/null +++ b/app/views/devise/passwords/new.html.erb @@ -0,0 +1,16 @@ +

Forgot your password?

+ +<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| %> + <%= render "devise/shared/error_messages", resource: resource %> + +
+ <%= f.label :email %>
+ <%= f.email_field :email, autofocus: true, autocomplete: "email" %> +
+ +
+ <%= f.submit "Send me reset password instructions" %> +
+<% end %> + +<%= render "devise/shared/links" %> diff --git a/app/views/devise/registrations/edit.html.erb b/app/views/devise/registrations/edit.html.erb new file mode 100644 index 000000000..85bf895cd --- /dev/null +++ b/app/views/devise/registrations/edit.html.erb @@ -0,0 +1,99 @@ +<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %> +<%= render "devise/shared/error_messages", resource: resource %> + +
+
+
+

Edit <%= resource_name.to_s.humanize %>

+
+
+ <%= f.label :email %>
+ <%= f.email_field :email, autofocus: true, autocomplete: "email",placeholder:"Email",class:"form-control" %> +
+ <%if current_page?(edit_tourist_registration_path)%> +
+ <%= f.label :first_name%>
+ <%= f.text_field :first_name, class:"form-control"%> +
+
+ <%= f.label :last_name%>
+ <%= f.text_field :last_name, class:"form-control"%> +
+
+ <%= f.label :birth_date%>
+ <%= f.date_field :birth_date, class:"form-control"%> +
+ <% elsif current_page?(edit_agency_registration_path) %> + <%= f.label :agency_name%>
+ <%= f.text_field :agency_name, class:"form-control"%> + <%elsif current_page?(edit_agency_registration_path)%> +
+ <%= f.label :first_name%>
+ <%= f.text_field :first_name, class:"form-control"%> +
+
+ <%= f.label :last_name%>
+ <%= f.text_field :last_name, class:"form-control"%> +
+
+ <%= f.label :birth_date%>
+ <%= f.date_field :birth_date, class:"form-control"%> +
+ <%end%> +
+ <%= f.label :address%>
+ <%= f.text_field :address, class:"form-control"%> +
+ +
+ <%= f.label :password %>
+ <% if @minimum_password_length %> + (<%= @minimum_password_length %> characters minimum) + <% end %>
+ <%= f.password_field :password, autocomplete: "new-password",placeholder:"Password",class:"form-control" %> +
+
+ <%= f.label :password_confirmation %>
+ <%= f.password_field :password_confirmation, autocomplete: "new-password",placeholder:"Confirm Password",class:"form-control" %> +
+
+ <%= f.label :current_password %>
+ <%= f.password_field :current_password, autocomplete: "current-password",placeholder:"Current Password",class:"form-control" %> +
+
+
+
+
+
+ <%= f.label "Cover Photo"%> + <%= f.file_field :cover_pic%> +
+ <%if current_user.cover_pic.attached?%> + + <%else%> + + <%end%> +
+ <%= f.label "Profile Photo"%> + <%= f.file_field :profile_pic%> +
+ <%if current_user.profile_pic.attached?%> + + <%else%> + + <%end%> +
+
+
+
+
+
+ <%= f.submit "Update",class:"btn btn-success w-80 font-weight-bold mt-4 mx-2" %> +
+
+<% end %> +
+
+ <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete, class:"btn btn-danger w-80" %>

+
+
diff --git a/app/views/devise/registrations/new.html.erb b/app/views/devise/registrations/new.html.erb new file mode 100644 index 000000000..7ca5dbf81 --- /dev/null +++ b/app/views/devise/registrations/new.html.erb @@ -0,0 +1,69 @@ + +


+<%= render 'shared/errors', obj: resource %> +
+
+
+ <% if current_page?(new_tourist_registration_path) %> + + <% elsif current_page?( new_agency_registration_path) %> + + <%end %> +
+
+ <% if current_page?(new_tourist_registration_path) %> +

Tourist Sign Up

+ <% elsif current_page?( new_agency_registration_path) %> +

Agency Sign Up

+ <%end %> + +
+ <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> +
+ <%= f.label :email %>
+ <%= f.email_field :email, autofocus: true, autocomplete: "email",class:"form-control" %> +
+ <%if current_page?(new_tourist_registration_path)%> +
+ <%= f.label :first_name%>
+ <%= f.text_field :first_name, class:"form-control"%> +
+
+ <%= f.label :last_name%>
+ <%= f.text_field :last_name, class:"form-control"%> +
+
+ <%= f.label :birth_date%>
+ <%= f.date_field :birth_date, class:"form-control"%> +
+ <% elsif current_page?(new_agency_registration_path) %> + <%= f.label :agency_name%>
+ <%= f.text_field :agency_name, class:"form-control"%> + <%end%> +
+ <%= f.label :address%>
+ <%= f.text_field :address, class:"form-control"%> +
+ +
+ <%= f.label :password %>
+ <% if @minimum_password_length %> + (<%= @minimum_password_length %> characters minimum) + <% end %>
+ <%= f.password_field :password, autocomplete: "new-password",placeholder:"Password",class:"form-control" %> +
+
+ <%= f.label :password_confirmation %>
+ <%= f.password_field :password_confirmation, autocomplete: "new-password",placeholder:"Confirm Password",class:"form-control" %> +
+
+
+ <%= f.submit "Sign up",class:"btn btn-dark w-100 font-weight-bold mt-2" %> +
+
+ <% end %> + <%= render "devise/shared/links" %> +
+
+
+
\ No newline at end of file diff --git a/app/views/devise/sessions/new.html.erb b/app/views/devise/sessions/new.html.erb new file mode 100644 index 000000000..11f30d2bd --- /dev/null +++ b/app/views/devise/sessions/new.html.erb @@ -0,0 +1,50 @@ + +


+
+
+
+ <% if resource_name == :tourist %> + + <% elsif resource_name == :agency %> + + <% elsif resource_name == :admin %> + + <%end %> +
+
+ <% if current_page?(new_tourist_session_path) %> +

Tourist Login

+ <% elsif current_page?( new_agency_session_path) %> +

Agency Login

+ <% elsif current_page?( new_admin_session_path) %> +

Admin Login

+ <%end %> +
+ <%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %> +
+ <%= f.label :email %>
+ <%= f.email_field :email, autofocus: true, autocomplete: "email",placeholder:"Email",class:"form-control" %> +
+
+ <%= f.label :password %>
+ <%= f.password_field :password, autocomplete: "current-password",placeholder:"Password",class:"form-control" %> +
+
+ <% if devise_mapping.rememberable? %> +
+ <%= f.check_box :remember_me %> + <%= f.label :remember_me %> +
+ <% end %> +
+
+
+ <%= f.submit "Log in",class:"btn btn-dark w-100 font-weight-bold mt-2" %> +
+
+ <% end %> + <%= render "devise/shared/links" %> +
+
+
+
diff --git a/app/views/devise/shared/_error_messages.html.erb b/app/views/devise/shared/_error_messages.html.erb new file mode 100644 index 000000000..ba7ab8870 --- /dev/null +++ b/app/views/devise/shared/_error_messages.html.erb @@ -0,0 +1,15 @@ +<% if resource.errors.any? %> +
+

+ <%= I18n.t("errors.messages.not_saved", + count: resource.errors.count, + resource: resource.class.model_name.human.downcase) + %> +

+
    + <% resource.errors.full_messages.each do |message| %> +
  • <%= message %>
  • + <% end %> +
+
+<% end %> diff --git a/app/views/devise/shared/_links.html.erb b/app/views/devise/shared/_links.html.erb new file mode 100644 index 000000000..823c58838 --- /dev/null +++ b/app/views/devise/shared/_links.html.erb @@ -0,0 +1,25 @@ +<%- if controller_name != 'sessions' %> + <%= link_to "Log in", new_session_path(resource_name) %>
+<% end %> + +<%- if devise_mapping.registerable? && controller_name != 'registrations' && resource_name != :admin %> + <%= link_to "Sign up", new_registration_path(resource_name) %>
+<% end %> + +<%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %> + <%= link_to "Forgot your password?", new_password_path(resource_name) %>
+<% end %> + +<%- if devise_mapping.confirmable? && controller_name != 'confirmations' %> + <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %>
+<% 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) %>
+<% 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) %>
+ <% end %> +<% end %> diff --git a/app/views/devise/unlocks/new.html.erb b/app/views/devise/unlocks/new.html.erb new file mode 100644 index 000000000..ffc34de8d --- /dev/null +++ b/app/views/devise/unlocks/new.html.erb @@ -0,0 +1,16 @@ +

Resend unlock instructions

+ +<%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %> + <%= render "devise/shared/error_messages", resource: resource %> + +
+ <%= f.label :email %>
+ <%= f.email_field :email, autofocus: true, autocomplete: "email" %> +
+ +
+ <%= f.submit "Resend unlock instructions" %> +
+<% end %> + +<%= render "devise/shared/links" %> diff --git a/app/views/home/index.html.erb b/app/views/home/index.html.erb new file mode 100644 index 000000000..6df3d6d75 --- /dev/null +++ b/app/views/home/index.html.erb @@ -0,0 +1,210 @@ + + + + +
+
+

SERVICES OFFERED

+
+ +
+
+
+
+

AMAZING TOURS

+

+

There are dozens of tours to choose from, while organizing a trip, and we’ll make sure your tour includes everything you need.

+
+
+
+
+

GREAT PRICES

+

+

All our tours and excursions are available at really affordable prices so you can always pick a great destination.

+
+
+
+
+

BEST SERVICES

+

+

Our aim is to provide you with the tour service of the top quality, and we'll do our best to find the suitable tour for you.

+
+
+
+
+
+ + + +
+

We Provide Only the Best Tours

+

We are committed to offering travel services of the highest quality

+
+ + +
+
+

MEET THE TEAM

+
+
+
+
+
+
+ +
+
+

Eileen de Guzman

+ Software Developer +
+ +
+
+ +
+
+
+ +
+
+

Leif Bersamina

+ Software Developer +
+ +
+
+
+
+
+ +
+
+

Joseph Ong

+ Software Developer +
+ +
+
+ +
+
+
+ +
+
+

Ronald Magno

+ Software Developer +
+ +
+
+
+
+
+ + + \ No newline at end of file diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 73221bbe8..45980d410 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -1,15 +1,67 @@ - - Rails Project + + + + + + + + + + + + + + + + + + + + + TravelZilla + <%= favicon_link_tag asset_path('favicon.ico') %> + <%= csrf_meta_tags %> <%= csp_meta_tag %> - + <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %> + - <%= yield %> + <%= render 'shared/nav_bar'%> + <% if notice %> +


+

<%= notice %>

+ <% end %> + <% if alert %> +


+

<%= alert %>

+ <% end %> + + <%= yield %> + + + + + + + + + + + + + + + + + + diff --git a/app/views/messages/_form.html.erb b/app/views/messages/_form.html.erb new file mode 100644 index 000000000..4ade1615f --- /dev/null +++ b/app/views/messages/_form.html.erb @@ -0,0 +1,23 @@ +<%= form_for(message, html:{class:"row gx-3 ui reply form", role: "form"}, url: messages_path, remote: true) do |form| %> + <% if message.errors.any? %> +
+

<%= pluralize(message.errors.count, "error") %> prohibited this message from being saved:

+ +
    + <% message.errors.each do |error| %> +
  • <%= error.full_message %>
  • + <% end %> +
+
+ <% end %> + + <%= form.hidden_field :chat_room_id, value: room.id %> + +
+ <%= form.text_field :body, placeholder: 'Type your message here...', class: 'form-control' %> +
+ +
+ <%= form.submit "Send", class: 'btn btn-success' %> +
+<% end %> diff --git a/app/views/messages/_message.html.erb b/app/views/messages/_message.html.erb new file mode 100644 index 000000000..22e07f272 --- /dev/null +++ b/app/views/messages/_message.html.erb @@ -0,0 +1,11 @@ +
+
+
+ <%=message.body%> +
+
+ <%=message.user.username%> +
+ +
+
\ No newline at end of file diff --git a/app/views/messages/_mine.html.erb b/app/views/messages/_mine.html.erb new file mode 100644 index 000000000..3edfb1183 --- /dev/null +++ b/app/views/messages/_mine.html.erb @@ -0,0 +1,11 @@ +
+
+
+ <%=message.body%> +
+
+ <%=message.user.username%> +
+ +
+
\ No newline at end of file diff --git a/app/views/messages/_theirs.html.erb b/app/views/messages/_theirs.html.erb new file mode 100644 index 000000000..14df3f828 --- /dev/null +++ b/app/views/messages/_theirs.html.erb @@ -0,0 +1,11 @@ +
+
+
+ <%=message.body%> +
+
+ <%=message.user.username%> +
+ +
+
\ No newline at end of file diff --git a/app/views/shared/_errors.html.erb b/app/views/shared/_errors.html.erb new file mode 100644 index 000000000..5c84e5f83 --- /dev/null +++ b/app/views/shared/_errors.html.erb @@ -0,0 +1,15 @@ +<% if obj.errors.any? %> +
+ +
+ <%end%> \ No newline at end of file diff --git a/app/views/shared/_nav_bar.html.erb b/app/views/shared/_nav_bar.html.erb new file mode 100644 index 000000000..58caaac73 --- /dev/null +++ b/app/views/shared/_nav_bar.html.erb @@ -0,0 +1,109 @@ + diff --git a/app/views/tourist_tours/index.html.erb b/app/views/tourist_tours/index.html.erb new file mode 100644 index 000000000..1033340b4 --- /dev/null +++ b/app/views/tourist_tours/index.html.erb @@ -0,0 +1,33 @@ +



+<% if @tourist_tours.count == 0 %> + +
+ <%= image_tag("journey-animate.svg")%> + +
+ +
+<%= link_to 'Travel Now', tours_path, class:"h1 text-secondary"%> +
+ +<%else%> + +

+
+<%@tourist_tours.each do |tourist_tour|%> +
+ <%if tourist_tour.tour.images.attached?%> + + <%end%> +
+

<%= tourist_tour.tour.name%>

+
Total Amount: <%= tourist_tour.amount_bought%>
+
Start Date: <%= tourist_tour.start_date%>
+
End Date: <%= tourist_tour.end_date%>
+ <%= link_to 'Details', tourist_tour_path(tourist_tour),class:"btn btn-secondary", method: :get%> +
+
+<%end%> +
+ +<%end%> \ No newline at end of file diff --git a/app/views/tourist_tours/new.html.erb b/app/views/tourist_tours/new.html.erb new file mode 100644 index 000000000..cccf188aa --- /dev/null +++ b/app/views/tourist_tours/new.html.erb @@ -0,0 +1,76 @@ +




+
+

<%=@tour.name%>

+
+
+
+ +
+
+ <%= form_with url: checkouts_path, local: false, remote: true do |f|%> +
+ <%= f.label :guest_quantity, id:"formId" %>
+ <%= f.number_field :quantity, id: 'guest_quantity', class:"form-control"%> +
+ +
+ <%= f.label 'Total Price', id:"formId" %>
+ <%= f.number_field :amount, readonly: true, id: 'total_price',class:"form-control", min: 1%> +
+ +
+ <%= f.label :start_date, id:"formId" %>
+ <%= f.date_field :start_date, id: 'start-date',class:"form-control"%> +
+ +
+ <%= f.label :end_date, id:"formId" %>
+ <%= f.date_field :end_date, readonly: true, id: 'end-date',class:"form-control"%> +
+ + <%= f.hidden_field :tour_id, value: @tour.id%> + <%= f.submit 'Avail', class:"btn btn-success mt-3"%> + <%end%> +
+
+
+
+
+

Location: <%=@tour.location%>

+

>Price: <%=@tour.price%>

+

>Duration: <%=@tour.duration%> day/s

+

Details:

+

<%=@tour.details%>

+
+
+
+ <%= javascript_pack_tag 'tourist_tour_new' %> \ No newline at end of file diff --git a/app/views/tourist_tours/show.html.erb b/app/views/tourist_tours/show.html.erb new file mode 100644 index 000000000..0c40e40f2 --- /dev/null +++ b/app/views/tourist_tours/show.html.erb @@ -0,0 +1,15 @@ +
+
+
+ <%if @tourist_tour.tour.images.attached?%> + <%@tourist_tour.tour.images.each do |img|%> + + <% end %> + <%end%> +
+
<%= @tourist_tour.tour.name%>
+

<%= @tourist_tour.tour.details.body%>

+ +
+
+
\ No newline at end of file diff --git a/app/views/tourists/show.html.erb b/app/views/tourists/show.html.erb new file mode 100644 index 000000000..44eb7b8ff --- /dev/null +++ b/app/views/tourists/show.html.erb @@ -0,0 +1,83 @@ +

+
+
+
+ <%if @tourist.cover_pic.attached?%> + + <%else%> + + <%end%> +

+
+
+
+ <%if @tourist.profile_pic.attached?%> + + <%else%> + + <%end%> + +
+

<%= @tourist.first_name%> <%=@tourist.last_name%>

+

Tourist

+ <% if @tourist == current_tourist%> + <%= button_to 'Edit Profile', edit_tourist_registration_path,class:"btn btn-info ", method: :get%> + <% end %> +
+
+
+
+
+
+

+
+
+
+
First Name
+
+
+ <%= @tourist.first_name%> +
+
+
+
+
+
Last Name
+
+
+ <%=@tourist.last_name%> +
+
+
+
+
+
Email
+
+
+ <%= @tourist.email%> +
+
+
+
+
+
Birth Date
+
+
+ <%= @tourist&.birth_date&.strftime('%b %d, %Y')%> +
+
+
+
+
+
Address
+
+
+ <%= @tourist.address%> +
+
+
+
+
+
+
+
diff --git a/app/views/tours/edit.html.erb b/app/views/tours/edit.html.erb new file mode 100644 index 000000000..2fc91548a --- /dev/null +++ b/app/views/tours/edit.html.erb @@ -0,0 +1,43 @@ +

+
+
+
+

Edit Tour


+ <%= render 'shared/errors', obj: @tour %> +
+ <%= form_with scope: :tour, model: @tour,method: :patch, local:true do |f|%> +
+ <%= f.text_field :name, autofocus: true,placeholder:"Package Name",class:"form-control" %> +
+
+ <%= f.number_field :price, step: 0.001,placeholder:"Price",class:"form-control" %> +
+
+ <%= f.text_field :location, placeholder: "Tour location" ,class:"form-control" %> +
+
+ <%= f.number_field :duration, step:1, placeholder: "Duration" ,class:"form-control" %> +
+
+ +
+
+
+ <%= f.label :"Select Hero Image",:class=>"form-label"%> + <%= f.file_field :images, multiple: true,:class=>"form-control form-control-lg image-upload"%> +
+
+
+ <%= f.label :"Tour Details", :class=>"form-label"%>
+ <%= f.rich_text_area :details, wrap: "hard", placeholder: "Tour Details here" ,:class=>"form-control blog-body"%> +
+
+ <%= link_to 'Cancel',tours_path, class:'btn btn-danger' %> + <%= f.submit "Update",class:"btn btn-success mx-3" %> +
+ <% end %> +
+
+
+
+<%= javascript_pack_tag 'image_preview' %> \ No newline at end of file diff --git a/app/views/tours/index.html.erb b/app/views/tours/index.html.erb new file mode 100644 index 000000000..60513f5f0 --- /dev/null +++ b/app/views/tours/index.html.erb @@ -0,0 +1,35 @@ +



+<% if @tours.count == 0 %> + +
+ <%= image_tag("journey-animate.svg")%> + +
+ +
+<%= link_to 'Create Tour', new_tour_path, class:"h1 text-secondary"%> +
+ +<%else%> +

+
+<% @tours.each do |tour|%> +
+ <% if tour.images.attached?%> + ... + <%end%> +
+

Name: <%= tour.name%>

+
Price: <%= tour.price%>
+
Location: <%= tour.location%>
+ <%if tourist_signed_in?%> + <%= link_to 'Show Package', tour_path(tour.id),class:"btn btn-primary"%> + <%else%> + <%= link_to 'Show', tour_path(tour),class:"btn btn-primary"%> + <%end%> +
+
+<%end%> +
+ +<%end%> diff --git a/app/views/tours/new.html.erb b/app/views/tours/new.html.erb new file mode 100644 index 000000000..402605769 --- /dev/null +++ b/app/views/tours/new.html.erb @@ -0,0 +1,43 @@ +

+
+
+
+

Create a new Tour


+ <%= render 'shared/errors', obj: @tour %> +
+ <%= form_with scope: :tour, url: tours_path, local:true do |f|%> +
+ <%= f.text_field :name, autofocus: true,placeholder:"Package Name",class:"form-control" %> +
+
+ <%= f.number_field :price, step: 0.001,placeholder:"Price",class:"form-control" %> +
+
+ <%= f.text_field :location, placeholder: "Tour location" ,class:"form-control" %> +
+
+ <%= f.number_field :duration, step:1, placeholder: "Number of Days" ,class:"form-control" %> +
+
+ +
+
+
+ <%= f.label :"Select Hero Image",:class=>"form-label"%> + <%= f.file_field :images, multiple: true,:class=>"form-control form-control-lg image-upload"%> +
+
+
+ <%= f.label :"Tour Details", :class=>"form-label"%>
+ <%= f.rich_text_area :details, wrap: "hard", placeholder: "Tour Details here" ,:class=>"form-control blog-body"%> +
+
+ <%= link_to 'Cancel',tours_path, class:'btn btn-danger' %> + <%= f.submit "Post",class:"btn btn-success mx-3" %> +
+ <% end %> +
+
+
+
+<%= javascript_pack_tag 'image_preview' %> \ No newline at end of file diff --git a/app/views/tours/show.html.erb b/app/views/tours/show.html.erb new file mode 100644 index 000000000..979f142ba --- /dev/null +++ b/app/views/tours/show.html.erb @@ -0,0 +1,63 @@ +




+
+
+

<%=@tour.name%>

+ <%if user_signed_in? && current_user&.type == 'Agency' %> + <%= link_to 'Edit', edit_tour_path, class:"btn btn-info"%> + <%elsif user_signed_in? && current_user&.type == 'Tourist'%> +
+ <%if user_signed_in? && current_user&.type != 'Agency' %> + <%=link_to chat_user_path(user_id: "#{@tour.agency.id}"), method: :post ,class: 'btn btn-primary me-4' do%> + Message Me + <%end%> + <%=link_to ' Avail', new_tourist_tour_path(@tour),class: 'btn btn-success'%> + <%end%> +
+ <%end%> +
+
+
+
+ +
+
+
+

Location: <%=@tour.location%>

+

Price: <%=@tour.price%>

+

Duration: <%=@tour.duration%>

+

Details:

+

<%=@tour.details%>

+
+
+
+
+
\ No newline at end of file diff --git a/app/views/travel_transactions/index.html.erb b/app/views/travel_transactions/index.html.erb new file mode 100644 index 000000000..bdf38c380 --- /dev/null +++ b/app/views/travel_transactions/index.html.erb @@ -0,0 +1,29 @@ +



+ + + + + + + + + + + + + <% @travel_transactions.each do |transaction|%> + + + + + + <%if !transaction.tourist_tour.start_date.nil?%> + + <%end%> + <%if !transaction.tourist_tour.end_date.nil?%> + + <%end%> + + <%end%> + +
Package NameTouristAgencyDate AvailedStart DateEnd Date
<%= transaction.tourist_tour.tour.name%><%= transaction.tourist_tour.tourist.email%><%= transaction.agency.email%><%= transaction.created_at.strftime('%b %d, %Y')%><%= transaction.tourist_tour.start_date.strftime('%b %d, %Y')%><%= transaction.tourist_tour.end_date.strftime('%b %d, %Y')%>
\ No newline at end of file diff --git a/config/credentials.yml.enc b/config/credentials.yml.enc index 13bec8b12..3797e37c6 100644 --- a/config/credentials.yml.enc +++ b/config/credentials.yml.enc @@ -1 +1 @@ -vGGT60HbIwlvjrOSj8QkGzQ+EqBcuWQJziz4siEGjSykZwqibwIc5BZChF2C3wBvxcuXygOObUvRGWcp+B58qkXoCUjwueUUhnkq4Utn0QcjV/5rmsVdVuLC2V7ZmxT/IKgviPzOa1Iz7PN7tBrqJTZoi6KrqYYYWRnXSvC0lLDkdqoKG35CEKRcdLtt385m4rK3JWWQXOVJXLSlQhCANW0XvXvZEJtm8E0LCwUx98SXoV8WRO//B4i7CUv56H61daxxSb9A940DcRkDR6dJLDOAyCJbuOGgsyoX3R4ybiK4xNzS9XXLmiUyXfdRxeAJAeB0Bthc4+8tymmiEd5Nlo8U5mwAN8U149b/5wBbw4vKMPkdNxCLMygLjC84rbMtJTrxCsdRHjQ+YbCrxirhEFQNHM2XyUoJ06WD--RYah7W4NWRhsH2Sf--TLLM8vHkIpbAHY802vtuIA== \ No newline at end of file +VpZ3b9VHg9mCmhG1sKHVqbonBKeeCjRVvm4JAxAiuleJA11OFk1xttBxZSd2fOJ+HtPkkl3nemazBQ5TDX9/EBExNsEVg1DDmiSwEA0kFESOfyjVR1Xpoz+WcVHUunydUvdM0cx3s7jtxWOKy72dGZZa/VayAVMGLRLfUHoYFuZwAJ3q+nEaVLxAGTciqjQnQYi15Jqagd0TWfjBP8pOqoEvjVm2xnFqkiWQbt9qU5WAO7b6N2xM5c1Jl4pygudfN8K2RlwSSKtI8JJMO3VjNt4HbCWFRRZxKr2Fp6TT2EsM7FzuBlK6MS/+6LAhMcIylQv77a6KbrktTVPX0yZfF44MMbUyXkFp629rPW8CF+tfP+X+nN8sMiyuc7I02ixcsx7IY7PuLWeyacASPCe1GgKkM6Vpr6uOCLHsIyym1C/nL1/SW3edLezQpV/ZMX8aPiqY+m5B4NqGWTvK1IHvRMO82El3tA6gYHJF11I1r+BltbGHkCZ+i7D29F9eYnajiazPiVqyyK9JBN8RgEfouid4uf41hZTrY4Rf7BlSy2OeSTbPnzPzFmYbrggJW1gxIBTBBQ5II7/1HqgaFzV13HW3PbZIneldAmrmXtss6EzRVRFKbGWZ3mmaTGDzuHqZa6zDzrHjF07C7ousN1dxeZbHsWsIMlh3LTVRXassmyI9SkkiSov6uw/5BCL9m/Wo3LtbhxfXtcBEU8It--QF1AlFmuZZ0WpUc0--Gkvn5+LTGq7BhNDm9maQYQ== \ No newline at end of file diff --git a/config/environments/development.rb b/config/environments/development.rb index 66df51f6f..f77c59e83 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -59,4 +59,5 @@ # Use an evented file watcher to asynchronously detect changes in source code, # routes, locales, etc. This feature depends on the listen gem. config.file_watcher = ActiveSupport::EventedFileUpdateChecker + config.action_mailer.default_url_options = { host: 'localhost', port: 3000 } end diff --git a/config/environments/production.rb b/config/environments/production.rb index 69afea11b..77ddb50bd 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -36,7 +36,7 @@ # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX # Store uploaded files on the local file system (see config/storage.yml for options). - config.active_storage.service = :local + config.active_storage.service = :amazon # Mount Action Cable outside main process or domain. # config.action_cable.mount_path = nil diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb new file mode 100644 index 000000000..a98ad2613 --- /dev/null +++ b/config/initializers/devise.rb @@ -0,0 +1,311 @@ +# frozen_string_literal: true + +# Assuming you have not yet modified this file, each configuration option below +# is set to its default value. Note that some are commented out while others +# are not: uncommented lines are intended to protect your configuration from +# breaking changes in upgrades (i.e., in the event that future versions of +# Devise change the default values for those options). +# +# 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 = '45ea502616488c88616e1b5a578ac46a7151aff872f47c7b3cbb2607445ba0a52726baef340953c1d820aa821edfe0045231489f19042c94a033536a6e717ef6' + + # ==> Controller configuration + # Configure the parent class to the devise controllers. + # config.parent_controller = 'DeviseController' + + # ==> 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. + # For API-only applications to support authentication "out-of-the-box", you will likely want to + # enable this with :database unless you are using a custom strategy. + # 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 12. If + # using other algorithms, it sets how many times you want the password to be hashed. + # The number of stretches used for generating the hashed password are stored + # with the hashed password. This allows you to change the stretches without + # invalidating existing passwords. + # + # 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 : 12 + + # Set up a pepper to generate the hashed password. + # config.pepper = 'e7dbc2411fdbdc86605f235229e3cea5c762a3eb163746e28a81e38e452f71e9b26cd5dc66e421eea949b8784f0221aa8946192f1609bb6fdf4b4388c0f1f234' + + # 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. + # You can also set it to nil, which will allow the user to access the website + # without confirming their account. + # 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' + + # ==> Turbolinks configuration + # If your app is using Turbolinks, Turbolinks::Controller needs to be included to make redirection work correctly: + # + # ActiveSupport.on_load(:devise_failure_app) do + # include Turbolinks::Controller + # end + + # ==> Configuration for :registerable + + # When set to false, does not sign a user in automatically after their password is + # changed. Defaults to true, so a user is signed in automatically after changing a password. + # config.sign_in_after_change_password = true +end diff --git a/config/locales/devise.en.yml b/config/locales/devise.en.yml new file mode 100644 index 000000000..ab1f07060 --- /dev/null +++ b/config/locales/devise.en.yml @@ -0,0 +1,65 @@ +# Additional translations at https://github.com/heartcombo/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 confirmation link to confirm your new email address." + updated: "Your account has been updated successfully." + updated_but_not_signed_in: "Your account has been updated successfully, but since your password was changed, you need to sign in again" + 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:" diff --git a/config/routes.rb b/config/routes.rb index c06383a17..badf52b96 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,3 +1,23 @@ Rails.application.routes.draw do + devise_for :tourists + devise_for :agencies + devise_for :admins, skip: :registration + # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html + + root "home#index" + + resources :admins + resources :agencies, only: [:index, :show, :update] + resources :tourists, only: :show + resources :tours, except: [:destroy] + resources :tourist_tours, except: [:new, :edit, :update] + resources :travel_transactions, only: [:index] + resources :chat_rooms, only: [:index, :show] + resources :messages, only: [:create] + resources :checkouts, only: [:create] + resources :webhooks, only: [:create] + post 'chat_user', to: 'chat_rooms#chat_user', as: 'chat_user' + get '/tourist_tours/new/:tour_id', to: 'tourist_tours#new', as: 'new_tourist_tour' + end diff --git a/config/storage.yml b/config/storage.yml index d32f76e8f..ecc7b0ad9 100644 --- a/config/storage.yml +++ b/config/storage.yml @@ -7,12 +7,12 @@ local: root: <%= Rails.root.join("storage") %> # Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) -# amazon: -# service: S3 -# access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> -# secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> -# region: us-east-1 -# bucket: your_own_bucket +amazon: + service: S3 + access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> + secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> + region: ap-southeast-1 + bucket: travel-go # Remember not to checkin your GCS keyfile to a repository # google: @@ -31,4 +31,4 @@ local: # mirror: # service: Mirror # primary: local -# mirrors: [ amazon, google, microsoft ] +# mirrors: [ amazon, google, microsoft ] \ No newline at end of file diff --git a/config/webpack/environment.js b/config/webpack/environment.js index d16d9af74..3cf52851b 100644 --- a/config/webpack/environment.js +++ b/config/webpack/environment.js @@ -1,3 +1,8 @@ const { environment } = require('@rails/webpacker') - +const webpack = require("webpack") +environment.plugins.append("Provide", new webpack.ProvidePlugin({ + $: 'jquery', + jQuery: 'jquery', + Popper: ['popper.js', 'default'] +})) module.exports = environment diff --git a/config/webpacker.yml b/config/webpacker.yml index 8581ac047..79711bc8d 100644 --- a/config/webpacker.yml +++ b/config/webpacker.yml @@ -53,7 +53,7 @@ development: compile: true # Verifies that correct packages and versions are installed by inspecting package.json, yarn.lock, and node_modules - check_yarn_integrity: true + check_yarn_integrity: false # Reference: https://webpack.js.org/configuration/dev-server/ dev_server: diff --git a/db/migrate/20210712140032_create_active_storage_tables.active_storage.rb b/db/migrate/20210712140032_create_active_storage_tables.active_storage.rb new file mode 100644 index 000000000..0b2ce257c --- /dev/null +++ b/db/migrate/20210712140032_create_active_storage_tables.active_storage.rb @@ -0,0 +1,27 @@ +# This migration comes from active_storage (originally 20170806125915) +class CreateActiveStorageTables < ActiveRecord::Migration[5.2] + def change + create_table :active_storage_blobs do |t| + t.string :key, null: false + t.string :filename, null: false + t.string :content_type + t.text :metadata + t.bigint :byte_size, null: false + t.string :checksum, null: false + t.datetime :created_at, null: false + + t.index [ :key ], unique: true + end + + create_table :active_storage_attachments do |t| + t.string :name, null: false + t.references :record, null: false, polymorphic: true, index: false + t.references :blob, null: false + + t.datetime :created_at, null: false + + t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true + t.foreign_key :active_storage_blobs, column: :blob_id + end + end +end diff --git a/db/migrate/20210712140502_devise_create_users.rb b/db/migrate/20210712140502_devise_create_users.rb new file mode 100644 index 000000000..a655fc42c --- /dev/null +++ b/db/migrate/20210712140502_devise_create_users.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +class DeviseCreateUsers < ActiveRecord::Migration[6.0] + def change + create_table :users do |t| + ## Database authenticatable + t.string :email, null: false, default: "" + t.string :encrypted_password, null: false, default: "" + t.integer :contact_number + t.string :address + t.string :type + ## Tourist + t.string :first_name + t.string :middle_name + t.string :last_name + t.date :birth_date + + ##Travel Agency + t.string :agency_name + t.decimal :average_rating + t.bigint :verified_by + t.boolean :active, default: true + + + ## 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 diff --git a/db/migrate/20210712140709_create_tours.rb b/db/migrate/20210712140709_create_tours.rb new file mode 100644 index 000000000..4626fcb0a --- /dev/null +++ b/db/migrate/20210712140709_create_tours.rb @@ -0,0 +1,13 @@ +class CreateTours < ActiveRecord::Migration[6.0] + def change + create_table :tours do |t| + t.belongs_to :agency + t.string :name + t.decimal :price + t.string :location + t.integer :duration + t.boolean :active, default: true + t.timestamps + end + end +end diff --git a/db/migrate/20210712140806_create_tourist_tours.rb b/db/migrate/20210712140806_create_tourist_tours.rb new file mode 100644 index 000000000..7998c80a2 --- /dev/null +++ b/db/migrate/20210712140806_create_tourist_tours.rb @@ -0,0 +1,13 @@ +class CreateTouristTours < ActiveRecord::Migration[6.0] + def change + create_table :tourist_tours do |t| + t.belongs_to :tourist + t.belongs_to :tour + t.integer :guest_quantity + t.decimal :amount_bought + t.date :start_date + t.date :end_date + t.timestamps + end + end +end diff --git a/db/migrate/20210712140856_create_travel_transactions.rb b/db/migrate/20210712140856_create_travel_transactions.rb new file mode 100644 index 000000000..cf0ab7747 --- /dev/null +++ b/db/migrate/20210712140856_create_travel_transactions.rb @@ -0,0 +1,10 @@ +class CreateTravelTransactions < ActiveRecord::Migration[6.0] + def change + create_table :travel_transactions do |t| + t.belongs_to :tourist_tour + t.belongs_to :agency + t.decimal :total_price + t.timestamps + end + end +end diff --git a/db/migrate/20210712140925_create_reviews.rb b/db/migrate/20210712140925_create_reviews.rb new file mode 100644 index 000000000..341478814 --- /dev/null +++ b/db/migrate/20210712140925_create_reviews.rb @@ -0,0 +1,11 @@ +class CreateReviews < ActiveRecord::Migration[6.0] + def change + create_table :reviews do |t| + t.belongs_to :agency + t.belongs_to :tourist + t.text :review + t.decimal :rating + t.timestamps + end + end +end diff --git a/db/migrate/20210716070806_create_chat_rooms.rb b/db/migrate/20210716070806_create_chat_rooms.rb new file mode 100644 index 000000000..a75fccfd5 --- /dev/null +++ b/db/migrate/20210716070806_create_chat_rooms.rb @@ -0,0 +1,7 @@ +class CreateChatRooms < ActiveRecord::Migration[6.0] + def change + create_table :chat_rooms do |t| + t.timestamps + end + end +end diff --git a/db/migrate/20210716070910_create_messages.rb b/db/migrate/20210716070910_create_messages.rb new file mode 100644 index 000000000..3a626712a --- /dev/null +++ b/db/migrate/20210716070910_create_messages.rb @@ -0,0 +1,10 @@ +class CreateMessages < ActiveRecord::Migration[6.0] + def change + create_table :messages do |t| + t.belongs_to :chat_room + t.belongs_to :user + t.text :body + t.timestamps + end + end +end diff --git a/db/migrate/20210716071040_create_action_text_tables.action_text.rb b/db/migrate/20210716071040_create_action_text_tables.action_text.rb new file mode 100644 index 000000000..5dfbd1561 --- /dev/null +++ b/db/migrate/20210716071040_create_action_text_tables.action_text.rb @@ -0,0 +1,14 @@ +# This migration comes from action_text (originally 20180528164100) +class CreateActionTextTables < ActiveRecord::Migration[6.0] + def change + create_table :action_text_rich_texts do |t| + t.string :name, null: false + t.text :body, size: :long + t.references :record, null: false, polymorphic: true, index: false + + t.timestamps + + t.index [ :record_type, :record_id, :name ], name: "index_action_text_rich_texts_uniqueness", unique: true + end + end +end diff --git a/db/migrate/20210716071208_create_chat_room_users.rb b/db/migrate/20210716071208_create_chat_room_users.rb new file mode 100644 index 000000000..015af7120 --- /dev/null +++ b/db/migrate/20210716071208_create_chat_room_users.rb @@ -0,0 +1,9 @@ +class CreateChatRoomUsers < ActiveRecord::Migration[6.0] + def change + create_table :chat_room_users do |t| + t.belongs_to :user + t.belongs_to :chat_room + t.timestamps + end + end +end diff --git a/db/migrate/20210716071248_add_column_to_users.rb b/db/migrate/20210716071248_add_column_to_users.rb new file mode 100644 index 000000000..c4c60c2c3 --- /dev/null +++ b/db/migrate/20210716071248_add_column_to_users.rb @@ -0,0 +1,5 @@ +class AddColumnToUsers < ActiveRecord::Migration[6.0] + def change + add_column :users, :approved, :boolean, default: false + end +end diff --git a/db/migrate/20210717023742_change_contact_type.rb b/db/migrate/20210717023742_change_contact_type.rb new file mode 100644 index 000000000..82d942297 --- /dev/null +++ b/db/migrate/20210717023742_change_contact_type.rb @@ -0,0 +1,5 @@ +class ChangeContactType < ActiveRecord::Migration[6.0] + def change + change_column :users, :contact_number, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index b10373ba6..b85b15c78 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,9 +10,135 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 0) do +ActiveRecord::Schema.define(version: 2021_07_17_023742) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" + create_table "action_text_rich_texts", force: :cascade do |t| + t.string "name", null: false + t.text "body" + t.string "record_type", null: false + t.bigint "record_id", null: false + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + t.index ["record_type", "record_id", "name"], name: "index_action_text_rich_texts_uniqueness", unique: true + end + + create_table "active_storage_attachments", force: :cascade do |t| + t.string "name", null: false + t.string "record_type", null: false + t.bigint "record_id", null: false + t.bigint "blob_id", null: false + t.datetime "created_at", null: false + t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id" + t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true + end + + create_table "active_storage_blobs", force: :cascade do |t| + t.string "key", null: false + t.string "filename", null: false + t.string "content_type" + t.text "metadata" + t.bigint "byte_size", null: false + t.string "checksum", null: false + t.datetime "created_at", null: false + t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true + end + + create_table "chat_room_users", force: :cascade do |t| + t.bigint "user_id" + t.bigint "chat_room_id" + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + t.index ["chat_room_id"], name: "index_chat_room_users_on_chat_room_id" + t.index ["user_id"], name: "index_chat_room_users_on_user_id" + end + + create_table "chat_rooms", force: :cascade do |t| + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + end + + create_table "messages", force: :cascade do |t| + t.bigint "chat_room_id" + t.bigint "user_id" + t.text "body" + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + t.index ["chat_room_id"], name: "index_messages_on_chat_room_id" + t.index ["user_id"], name: "index_messages_on_user_id" + end + + create_table "reviews", force: :cascade do |t| + t.bigint "agency_id" + t.bigint "tourist_id" + t.text "review" + t.decimal "rating" + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + t.index ["agency_id"], name: "index_reviews_on_agency_id" + t.index ["tourist_id"], name: "index_reviews_on_tourist_id" + end + + create_table "tourist_tours", force: :cascade do |t| + t.bigint "tourist_id" + t.bigint "tour_id" + t.integer "guest_quantity" + t.decimal "amount_bought" + t.date "start_date" + t.date "end_date" + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + t.index ["tour_id"], name: "index_tourist_tours_on_tour_id" + t.index ["tourist_id"], name: "index_tourist_tours_on_tourist_id" + end + + create_table "tours", force: :cascade do |t| + t.bigint "agency_id" + t.string "name" + t.decimal "price" + t.string "location" + t.integer "duration" + t.boolean "active", default: true + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + t.index ["agency_id"], name: "index_tours_on_agency_id" + end + + create_table "travel_transactions", force: :cascade do |t| + t.bigint "tourist_tour_id" + t.bigint "agency_id" + t.decimal "total_price" + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + t.index ["agency_id"], name: "index_travel_transactions_on_agency_id" + t.index ["tourist_tour_id"], name: "index_travel_transactions_on_tourist_tour_id" + end + + create_table "users", force: :cascade do |t| + t.string "email", default: "", null: false + t.string "encrypted_password", default: "", null: false + t.string "contact_number" + t.string "address" + t.string "type" + t.string "first_name" + t.string "middle_name" + t.string "last_name" + t.date "birth_date" + t.string "agency_name" + t.decimal "average_rating" + t.bigint "verified_by" + t.boolean "active", default: true + t.string "reset_password_token" + t.datetime "reset_password_sent_at" + t.datetime "remember_created_at" + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + t.boolean "approved", default: false + 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 + + add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id" end diff --git a/package.json b/package.json index b56a1d18e..dbe838d5f 100644 --- a/package.json +++ b/package.json @@ -2,10 +2,15 @@ "name": "rails_project", "private": true, "dependencies": { + "@popperjs/core": "^2.9.2", "@rails/actioncable": "^6.0.0", + "@rails/actiontext": "^6.0.3-6", "@rails/activestorage": "^6.0.0", "@rails/ujs": "^6.0.0", "@rails/webpacker": "4.3.0", + "bootstrap": "^5.0.2", + "jquery": "^3.6.0", + "trix": "^1.2.0", "turbolinks": "^5.2.0" }, "version": "0.1.0", diff --git a/spec/models/chat_room_spec.rb b/spec/models/chat_room_spec.rb new file mode 100644 index 000000000..6f48ed43b --- /dev/null +++ b/spec/models/chat_room_spec.rb @@ -0,0 +1,19 @@ +require 'rails_helper' + +RSpec.describe ChatRoom, type: :model do + let!(:chat_room) { described_class.new } + + context 'with validations of relationship' do + it 'belongs to messages' do + expect(chat_room).to have_many(:messages) + end + + it 'belongs to a chat room users' do + expect(chat_room).to have_many(:chat_room_users) + end + + it 'belongs to a users' do + expect(chat_room).to have_many(:users) + end + end +end diff --git a/spec/models/chat_room_user_spec.rb b/spec/models/chat_room_user_spec.rb new file mode 100644 index 000000000..ff888877f --- /dev/null +++ b/spec/models/chat_room_user_spec.rb @@ -0,0 +1,15 @@ +require 'rails_helper' + +RSpec.describe ChatRoomUser, type: :model do + let!(:chat_room_user) { described_class.new } + + context 'with validations of relationship' do + it 'belongs to tourist tour' do + expect(chat_room_user).to belong_to(:user) + end + + it 'belongs to a agency' do + expect(chat_room_user).to belong_to(:chat_room) + end + end +end diff --git a/spec/models/message_spec.rb b/spec/models/message_spec.rb new file mode 100644 index 000000000..f96091a4a --- /dev/null +++ b/spec/models/message_spec.rb @@ -0,0 +1,15 @@ +require 'rails_helper' + +RSpec.describe Message, type: :model do + let!(:message) { described_class.new } + + context 'with validations of relationship' do + it 'belongs to chat room' do + expect(message).to belong_to(:chat_room) + end + + it 'belongs to a user' do + expect(message).to belong_to(:user) + end + end +end diff --git a/spec/models/review_spec.rb b/spec/models/review_spec.rb new file mode 100644 index 000000000..f78c523f4 --- /dev/null +++ b/spec/models/review_spec.rb @@ -0,0 +1,21 @@ +require 'rails_helper' + +RSpec.describe Review, type: :model do + let!(:review) { described_class.new } + + context 'with validations' do + it 'must have a valid rating' do + expect(review).to validate_numericality_of(:rating).is_greater_than_or_equal_to(0) + end + end + + context 'with validations of relationship' do + it 'belongs to agency' do + expect(review).to belong_to(:agency) + end + + it 'belongs to a tourist' do + expect(review).to belong_to(:tourist) + end + end +end diff --git a/spec/models/tour_spec.rb b/spec/models/tour_spec.rb new file mode 100644 index 000000000..587e85499 --- /dev/null +++ b/spec/models/tour_spec.rb @@ -0,0 +1,41 @@ +require 'rails_helper' + +RSpec.describe Tour, type: :model do + let!(:tour) { described_class.new } + + context 'with validations' do + it 'is not valid without name' do + expect(tour).to validate_presence_of(:name) + end + + it 'must have atlease 1 character' do + expect(tour).to validate_length_of(:name) + .is_at_least(1) + .on(:create) + end + + it 'is not valid without location' do + expect(tour).to validate_presence_of(:location) + end + + it 'must have at least 1 character' do + expect(tour).to validate_length_of(:location) + .is_at_least(1) + .on(:create) + end + + it 'must have a valid price' do + expect(tour).to validate_numericality_of(:price).is_greater_than_or_equal_to(0) + end + + it 'must have a valid duration' do + expect(tour).to validate_numericality_of(:duration).is_greater_than_or_equal_to(1) + end + end + + context 'with validations of relationship' do + it 'belongs to agency' do + expect(tour).to belong_to(:agency) + end + end +end diff --git a/spec/models/tourist_tour_spec.rb b/spec/models/tourist_tour_spec.rb new file mode 100644 index 000000000..6ac39ca8f --- /dev/null +++ b/spec/models/tourist_tour_spec.rb @@ -0,0 +1,15 @@ +require 'rails_helper' + +RSpec.describe TouristTour, type: :model do + let!(:tourist_tour) { described_class.new } + + context 'with validations' do + it 'must have a valid guest quantity' do + expect(tourist_tour).to validate_numericality_of(:guest_quantity).is_greater_than_or_equal_to(1) + end + + it 'must have a valid amount bought' do + expect(tourist_tour).to validate_numericality_of(:amount_bought).is_greater_than_or_equal_to(0) + end + end +end diff --git a/spec/models/travel_transaction_spec.rb b/spec/models/travel_transaction_spec.rb new file mode 100644 index 000000000..8de15be20 --- /dev/null +++ b/spec/models/travel_transaction_spec.rb @@ -0,0 +1,21 @@ +require 'rails_helper' + +RSpec.describe TravelTransaction, type: :model do + let!(:travel_transaction) { described_class.new } + + context 'with validations' do + it 'must have a valid amount bought' do + expect(travel_transaction).to validate_numericality_of(:total_price).is_greater_than_or_equal_to(1) + end + end + + context 'with validations of relationship' do + it 'belongs to tourist tour' do + expect(travel_transaction).to belong_to(:tourist_tour) + end + + it 'belongs to a agency' do + expect(travel_transaction).to belong_to(:agency) + end + end +end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 69a565b8e..115f2d25f 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -21,7 +21,7 @@ config.use_transactional_fixtures = false config.infer_spec_type_from_file_location! config.filter_rails_from_backtrace! - + config.include Devise::Test::IntegrationHelpers config.include FactoryBot::Syntax::Methods config.before(:suite) do diff --git a/spec/requests/admins_controllers_spec.rb b/spec/requests/admins_controllers_spec.rb new file mode 100644 index 000000000..8250c0603 --- /dev/null +++ b/spec/requests/admins_controllers_spec.rb @@ -0,0 +1,38 @@ +require 'rails_helper' + +RSpec.describe 'AdminsControllers', type: :request do + let!(:admin) { Admin.create(email: 'admin@email.com', first_name: 'Admin', last_name: 'Last', password: '1234567') } + + before do + sign_in(admin) + end + + context 'when GET admins' do + it 'shows the index page' do + get admins_path + expect(response).to have_http_status(:ok) + end + + it 'shows the selected admin' do + get admin_path(admin) + expect(response).to have_http_status(:ok) + end + + it 'shows the new admin template' do + get new_admin_path + expect(response).to have_http_status(:ok) + end + end + + context 'when POST and PATCH admins' do + it 'creates a new admin' do + post admins_path, params: { admin: { email: 'admin2@email.com', first_name: 'Admin', last_name: 'Last', birth_date: Date.new(1999, 10, 10), address: 'Kyoto, Japan', password: '1234567' } } + expect(response).to redirect_to(admins_path) + end + + it 'updates a new admin' do + patch admin_path(admin), params: { admin: { email: 'admin4@email.com', first_name: 'Admin2', last_name: 'Last', birth_date: Date.new(1999, 10, 10), address: 'Kyoto, Japan', password: '1234567' } } + expect(response).to redirect_to(admin_path(admin)) + end + end +end diff --git a/spec/requests/agencies_controllers_spec.rb b/spec/requests/agencies_controllers_spec.rb new file mode 100644 index 000000000..764601dcb --- /dev/null +++ b/spec/requests/agencies_controllers_spec.rb @@ -0,0 +1,27 @@ +require 'rails_helper' + +RSpec.describe 'AgenciesControllers', type: :request do + let!(:agency) { Agency.create(email: 'agency@email.com', agency_name: 'AgencyOne', address: 'address', password: '1234567') } + let!(:admin) { Admin.create(email: 'admin@email.com', first_name: 'Admin', last_name: 'Last', password: '1234567') } + + before { sign_in(admin) } + + context 'when GET agencies' do + it 'gets all agencies' do + get agencies_path + expect(response).to have_http_status(:ok) + end + + it 'shows a specific agency' do + get agency_path(agency) + expect(response).to have_http_status(:ok) + end + end + + context 'when PATCH agencies' do + it 'updates a specific agency' do + patch agency_path(agency), params: { agency: { email: 'agency5@email.com', agency_name: 'AgencyOne', password: '1234567', verified_by: admin.id } } + expect(response).to redirect_to(admins_path) + end + end +end diff --git a/spec/requests/chat_rooms_controllers_spec.rb b/spec/requests/chat_rooms_controllers_spec.rb new file mode 100644 index 000000000..5fc142cb2 --- /dev/null +++ b/spec/requests/chat_rooms_controllers_spec.rb @@ -0,0 +1,45 @@ +require 'rails_helper' + +RSpec.describe 'ChatRoomsControllers', type: :request do + let!(:tourist) { create(:tourist) } + let!(:tourist2) { create(:tourist) } + let!(:agency) { create(:approved_agency) } + let!(:chat_room) { ChatRoom.create } + + before do + chat_room.users << tourist + chat_room.users << agency + end + + describe 'GET chat_rooms#index path' do + it 'returns a redirect response if not logged_in' do + get chat_rooms_path + expect(response).to have_http_status(:found) + end + + it 'returns a success response if logged_in' do + sign_in(tourist) + get chat_rooms_path + expect(response).to have_http_status(:ok) + end + end + + describe 'GET /chat_rooms/:id show path request response for different users' do + it 'returns a redirect response if not logged_in' do + get chat_room_path(chat_room) + expect(response).to have_http_status(:found) + end + + it 'returns a success response if logged_in user owns the chatroom' do + sign_in(tourist) + get chat_room_path(chat_room) + expect(response).to have_http_status(:ok) + end + + it 'returns a redirect response if logged_in user is not the owener of the chatroom' do + sign_in(tourist2) + get chat_room_path(chat_room) + expect(response).to have_http_status(:found) + end + end +end diff --git a/spec/requests/tourist_tours_controllers_spec.rb b/spec/requests/tourist_tours_controllers_spec.rb new file mode 100644 index 000000000..46467830d --- /dev/null +++ b/spec/requests/tourist_tours_controllers_spec.rb @@ -0,0 +1,84 @@ +require 'rails_helper' + +RSpec.describe 'TouristToursControllers', type: :request do + let!(:admin) { create(:admin) } + let!(:tourist) { create(:tourist) } + let!(:agency) { create(:approved_agency) } + let!(:tour) { create(:tour, agency: agency) } + let!(:tourist_tour) { create(:tourist_tour, tourist: tourist, tour: tour) } + + describe 'GET tourist_tour index path request response for different users' do + it 'returns a redirect response if not logged_in ' do + get tourist_tours_path + expect(response).to have_http_status(:found) + end + + it 'returns a success response if current_user is tourist ' do + sign_in(tourist) + get tourist_tours_path + expect(response).to have_http_status(:ok) + end + + it 'returns a redirect response if current_user is agency ' do + sign_in(agency) + get tourist_tours_path + expect(response).to have_http_status(:found) + end + + it 'returns a redirect response if current_user is admin ' do + sign_in(admin) + get tourist_tours_path + expect(response).to have_http_status(:found) + end + end + + describe 'GET /tourist_tours/new/:tour_id path (new tourist_tour) for different users' do + it 'returns a redirect response if not logged_in ' do + get new_tourist_tour_path(tour) + expect(response).to have_http_status(:found) + end + + it 'returns a redirect response if current_user is agency ' do + sign_in(agency) + get new_tourist_tour_path(tour) + expect(response).to have_http_status(:found) + end + + it 'returns a redirect response if current_user is admin ' do + sign_in(admin) + get new_tourist_tour_path(tour) + expect(response).to have_http_status(:found) + end + + it 'returns a success response if current_user is tourist ' do + sign_in(tourist) + get new_tourist_tour_path(tour) + expect(response).to have_http_status(:ok) + end + end + + describe 'GET /tourist_tours/:id path (show tourist tour) for different users' do + it 'returns a redirect response if not logged_in ' do + get tourist_tours_path(tourist_tour) + expect(response).to have_http_status(:unauthorized) + end + + it 'returns a redirect response if current_user is agency ' do + sign_in(agency) + get tourist_tours_path(tourist_tour) + expect(response).to have_http_status(:unauthorized) + end + + it 'returns a redirect response if current_user is admin ' do + sign_in(admin) + get tourist_tours_path(tourist_tour) + expect(response).to have_http_status(:unauthorized) + end + + it 'returns a success response if current_user is tourist ' do + sign_in(tourist) + get tourist_tours_path(tourist_tour) + expect(response).to have_http_status(:ok) + end + end +end diff --git a/spec/requests/tourists_controllers_spec.rb b/spec/requests/tourists_controllers_spec.rb new file mode 100644 index 000000000..61c9ffb38 --- /dev/null +++ b/spec/requests/tourists_controllers_spec.rb @@ -0,0 +1,14 @@ +require 'rails_helper' + +RSpec.describe 'TouristsControllers', type: :request do + let!(:tourist) { Tourist.create(email: 'tourist@email.com', first_name: 'TouristOne', last_name: 'Tourist', address: 'Kyoto, Japan', birth_date: Date.new(1999, 8, 10), password: '1234567') } + + before { sign_in(tourist) } + + context 'when GET tourists' do + it 'works! (now write some real specs)' do + get tourist_path(tourist) + expect(response).to have_http_status(:ok) + end + end +end diff --git a/spec/requests/tours_controllers_spec.rb b/spec/requests/tours_controllers_spec.rb new file mode 100644 index 000000000..f9f4bfb7d --- /dev/null +++ b/spec/requests/tours_controllers_spec.rb @@ -0,0 +1,42 @@ +require 'rails_helper' + +RSpec.describe 'ToursControllers', type: :request do + let!(:agency) { create(:approved_agency) } + let!(:tour) { create(:tour, agency: agency) } + + before { sign_in(agency) } + + context 'when GET in tours' do + it 'gets all active tours' do + get tours_path + expect(response).to have_http_status(:ok) + end + + it 'gets the specifc tour' do + get tour_path(tour) + expect(response).to have_http_status(:ok) + end + + it 'gets the new tour template' do + get new_tour_path + expect(response).to have_http_status(:ok) + end + + it 'gets the edit tour template' do + get edit_tour_path(tour) + expect(response).to have_http_status(:ok) + end + end + + context 'when POST and PATCH tours' do + it 'posts a new tour' do + post tours_path, params: { tour: { name: 'Boracay', location: 'Boracay', duration: 3, price: 300, agency: agency } } + expect(response).to redirect_to(tours_path) + end + + it 'updates a tour' do + patch tour_path(tour), params: { tour: { name: 'Boracay', location: 'Boracay', duration: 3, price: 300, agency: agency } } + expect(response).to redirect_to(tour_path) + end + end +end diff --git a/spec/requests/travel_transactions_controllers_spec.rb b/spec/requests/travel_transactions_controllers_spec.rb new file mode 100644 index 000000000..d463ff125 --- /dev/null +++ b/spec/requests/travel_transactions_controllers_spec.rb @@ -0,0 +1,34 @@ +require 'rails_helper' + +RSpec.describe 'TravelTransactionsControllers', type: :request do + let!(:admin) { create(:admin) } + let!(:tourist) { create(:tourist) } + let!(:agency) { create(:approved_agency) } + let!(:tourist_tour) { create(:tourist_tour, tourist: tourist) } + let(:travel_transaction) { create(:travel_transaction, tourist_tour: tourist_tour, agency: agency) } + + describe 'GET /travel_transactions index path request response for different users' do + it 'returns a redirect response if not logged_in ' do + get travel_transactions_path + expect(response).to have_http_status(:found) + end + + it 'returns a success response if current_user is tourist ' do + sign_in(tourist) + get travel_transactions_path + expect(response).to have_http_status(:ok) + end + + it 'returns a success response if current_user is agency ' do + sign_in(agency) + get travel_transactions_path + expect(response).to have_http_status(:ok) + end + + it 'returns a success response if current_user is admin ' do + sign_in(admin) + get travel_transactions_path + expect(response).to have_http_status(:ok) + end + end +end diff --git a/test/factories/admins.rb b/test/factories/admins.rb new file mode 100644 index 000000000..34a7c0735 --- /dev/null +++ b/test/factories/admins.rb @@ -0,0 +1,10 @@ +FactoryBot.define do + factory :admin do + email { "#{first_name.gsub(' ', '')}@email.com" } + password { 'secure123' } + address { 'Antipolo City' } + approved { true } + first_name { 'admin' } + type { 'Admin' } + end +end diff --git a/test/factories/agencies.rb b/test/factories/agencies.rb new file mode 100644 index 000000000..c2241c63e --- /dev/null +++ b/test/factories/agencies.rb @@ -0,0 +1,17 @@ +FactoryBot.define do + factory :agency do + sequence(:email) { |n| "#{agency_name.gsub(' ', '')}#{n}@email.com" } + password { 'secure123' } + address { 'Antipolo City' } + agency_name { 'Tourist Travel Tours' } + type { 'Agency' } + contact_number { '9307176311' } + average_rating { 5 } + + trait :approved do + approved { true } + end + + factory :approved_agency, traits: [:approved] + end +end diff --git a/test/factories/tourist_tours.rb b/test/factories/tourist_tours.rb new file mode 100644 index 000000000..4b43a9848 --- /dev/null +++ b/test/factories/tourist_tours.rb @@ -0,0 +1,10 @@ +FactoryBot.define do + factory :tourist_tour do + guest_quantity { 2 } + amount_bought { 7998 } + start_date { '2021-07-16' } + end_date { '2021-07-19' } + tourist + tour + end +end diff --git a/test/factories/tourists.rb b/test/factories/tourists.rb new file mode 100644 index 000000000..0d1aa4d5d --- /dev/null +++ b/test/factories/tourists.rb @@ -0,0 +1,13 @@ +FactoryBot.define do + factory :tourist do + sequence(:email) { |n| "#{first_name.gsub(' ', '')}#{n}@email.com" } + password { 'secure123' } + address { 'Antipolo City' } + approved { true } + first_name { 'Tourist' } + middle_name { 'New' } + last_name { 'Gala' } + birth_date { '2006-01-01' } + type { 'Tourist' } + end +end diff --git a/test/factories/tours.rb b/test/factories/tours.rb new file mode 100644 index 000000000..56dcf2082 --- /dev/null +++ b/test/factories/tours.rb @@ -0,0 +1,9 @@ +FactoryBot.define do + factory :tour do + name { 'Trip to Boracay 3 days and 2 nights' } + price { 3999 } + location { 'Boracay' } + duration { 3 } + agency + end +end diff --git a/test/factories/travel_transactions.rb b/test/factories/travel_transactions.rb new file mode 100644 index 000000000..ec288ca50 --- /dev/null +++ b/test/factories/travel_transactions.rb @@ -0,0 +1,7 @@ +FactoryBot.define do + factory :travel_transaction do + total_price { 19_995 } + tourist_tour + agency + end +end diff --git a/test/fixtures/action_text/rich_texts.yml b/test/fixtures/action_text/rich_texts.yml new file mode 100644 index 000000000..8b371ea60 --- /dev/null +++ b/test/fixtures/action_text/rich_texts.yml @@ -0,0 +1,4 @@ +# one: +# record: name_of_fixture (ClassOfFixture) +# name: content +# body:

In a million stars!

diff --git a/yarn.lock b/yarn.lock index 641111eee..bfdad4242 100644 --- a/yarn.lock +++ b/yarn.lock @@ -836,11 +836,23 @@ mkdirp "^1.0.4" rimraf "^3.0.2" +"@popperjs/core@^2.9.2": + version "2.9.2" + resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.9.2.tgz#adea7b6953cbb34651766b0548468e743c6a2353" + integrity sha512-VZMYa7+fXHdwIq1TDhSXoVmSPEGM/aa+6Aiq3nVVJ9bXr24zScr+NlKFKC3iPljA7ho/GAZr+d2jOf5GIRC30Q== + "@rails/actioncable@^6.0.0": version "6.1.1" resolved "https://registry.yarnpkg.com/@rails/actioncable/-/actioncable-6.1.1.tgz#e980b2ea1c62cae17bcddad37deb6ba88d498b63" integrity sha512-A8toxvSuzK/wX9uMwyV6T1EYzlqzIJNrO8XiNCSNsmKresdX2cbAjxFdMu9haaPJPjl3aC2FKRJeVCUWVtLPAQ== +"@rails/actiontext@^6.0.3-6": + version "6.1.4" + resolved "https://registry.yarnpkg.com/@rails/actiontext/-/actiontext-6.1.4.tgz#ed8c7d2b68d66205301f4538ce65d04c48031f6b" + integrity sha512-KG+VCofmfZAhW1x1xFxMC2sZtvwbx+XX0Y+dzeBFjKU1jZMM0RNytJf5EiuHtD9fL71zz6/nMT7FjwOFenSA7Q== + dependencies: + "@rails/activestorage" "^6.0.0" + "@rails/activestorage@^6.0.0": version "6.1.1" resolved "https://registry.yarnpkg.com/@rails/activestorage/-/activestorage-6.1.1.tgz#c7e9c849ab9487c9914bd0bb447467cb4520bc56" @@ -1489,6 +1501,11 @@ boolbase@^1.0.0, boolbase@~1.0.0: resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= +bootstrap@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-5.0.2.tgz#aff23d5e0e03c31255ad437530ee6556e78e728e" + integrity sha512-1Ge963tyEQWJJ+8qtXFU6wgmAVj9gweEjibUdbmcCEYsn38tVwRk8107rk2vzt6cfQcRr3SlZ8aQBqaD8aqf+Q== + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -4018,6 +4035,11 @@ jest-worker@^25.4.0: merge-stream "^2.0.0" supports-color "^7.0.0" +jquery@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.6.0.tgz#c72a09f15c1bdce142f49dbf1170bdf8adac2470" + integrity sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw== + js-base64@^2.1.8: version "2.6.4" resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.6.4.tgz#f4e686c5de1ea1f867dbcad3d46d969428df98c4" @@ -7097,6 +7119,11 @@ trim-newlines@^1.0.0: resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" integrity sha1-WIeWa7WCpFA6QetST301ARgVphM= +trix@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/trix/-/trix-1.3.1.tgz#ccce8d9e72bf0fe70c8c019ff558c70266f8d857" + integrity sha512-BbH6mb6gk+AV4f2as38mP6Ucc1LE3OD6XxkZnAgPIduWXYtvg2mI3cZhIZSLqmMh9OITEpOBCCk88IVmyjU7bA== + "true-case-path@^1.0.2": version "1.0.3" resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-1.0.3.tgz#f813b5a8c86b40da59606722b144e3225799f47d"