├── log
└── .keep
├── storage
└── .keep
├── tmp
├── .keep
└── pids
│ └── .keep
├── vendor
└── .keep
├── lib
├── assets
│ └── .keep
└── tasks
│ └── .keep
├── public
├── favicon.ico
├── apple-touch-icon.png
├── apple-touch-icon-precomposed.png
├── robots.txt
├── 500.html
├── 422.html
└── 404.html
├── .ruby-version
├── app
├── assets
│ ├── builds
│ │ └── .keep
│ ├── images
│ │ └── .keep
│ ├── config
│ │ └── manifest.js
│ └── stylesheets
│ │ ├── host
│ │ └── listings.scss
│ │ ├── static_pages.scss
│ │ ├── users
│ │ └── omniauth_callbacks.scss
│ │ └── application.css
├── models
│ ├── concerns
│ │ └── .keep
│ ├── application_record.rb
│ ├── notification.rb
│ ├── photo.rb
│ ├── message.rb
│ ├── event.rb
│ ├── bed.rb
│ ├── room.rb
│ ├── reservation.rb
│ ├── calendar_event.rb
│ ├── listing.rb
│ ├── user.rb
│ └── book_listing.rb
├── controllers
│ ├── concerns
│ │ └── .keep
│ ├── application_controller.rb
│ ├── static_pages_controller.rb
│ ├── host
│ │ ├── reservations_controller.rb
│ │ ├── photos_controller.rb
│ │ ├── rooms_controller.rb
│ │ ├── merchant_settings_controller.rb
│ │ └── listings_controller.rb
│ ├── listings_controller.rb
│ ├── webhooks_controller.rb
│ ├── users
│ │ └── omniauth_callbacks_controller.rb
│ ├── messages_controller.rb
│ └── reservations_controller.rb
├── views
│ ├── layouts
│ │ ├── mailer.text.erb
│ │ ├── mailer.html.erb
│ │ └── application.html.erb
│ ├── message_mailer
│ │ ├── new_message.html.erb
│ │ └── new_message.text.erb
│ ├── static_pages
│ │ └── home.html.erb
│ ├── host
│ │ ├── merchant_settings
│ │ │ ├── connected.html.erb
│ │ │ └── index.html.erb
│ │ ├── listings
│ │ │ ├── show.html.erb
│ │ │ ├── edit.html.erb
│ │ │ ├── new.html.erb
│ │ │ └── index.html.erb
│ │ ├── photos
│ │ │ └── index.html.erb
│ │ ├── reservations
│ │ │ └── show.html.erb
│ │ └── rooms
│ │ │ ├── index.html.erb
│ │ │ └── _form.html.erb
│ ├── shared
│ │ ├── _errors.html.erb
│ │ └── _notices.html.erb
│ ├── reservation_mailer
│ │ ├── guest_booked.text.erb
│ │ ├── host_booked.text.erb
│ │ ├── guest_booked.html.erb
│ │ └── host_booked.html.erb
│ ├── devise
│ │ ├── mailer
│ │ │ ├── password_change.html.erb
│ │ │ ├── confirmation_instructions.html.erb
│ │ │ ├── unlock_instructions.html.erb
│ │ │ ├── email_changed.html.erb
│ │ │ └── reset_password_instructions.html.erb
│ │ ├── shared
│ │ │ ├── _error_messages.html.erb
│ │ │ └── _links.html.erb
│ │ ├── unlocks
│ │ │ └── new.html.erb
│ │ ├── passwords
│ │ │ ├── new.html.erb
│ │ │ └── edit.html.erb
│ │ ├── confirmations
│ │ │ └── new.html.erb
│ │ ├── registrations
│ │ │ ├── edit.html.erb
│ │ │ └── new.html.erb
│ │ └── sessions
│ │ │ └── new.html.erb
│ ├── reservations
│ │ ├── show.html.erb
│ │ ├── new.html.erb
│ │ └── index.html.erb
│ ├── listings
│ │ ├── show.html.erb
│ │ └── index.html.erb
│ └── messages
│ │ └── index.html.erb
├── helpers
│ ├── messages_helper.rb
│ ├── webhooks_helper.rb
│ ├── host
│ │ ├── photos_helper.rb
│ │ ├── rooms_helper.rb
│ │ ├── listings_helper.rb
│ │ ├── reservations_helper.rb
│ │ └── merchant_settings_helper.rb
│ ├── reservations_helper.rb
│ ├── static_pages_helper.rb
│ ├── listings_helper.rb
│ ├── users
│ │ └── omniauth_callbacks_helper.rb
│ └── application_helper.rb
├── javascript
│ ├── channels
│ │ ├── index.js
│ │ └── consumer.js
│ ├── controllers
│ │ ├── application.js
│ │ ├── room_controller.js
│ │ ├── menu_controller.js
│ │ ├── index.js
│ │ ├── listing_map_controller.js
│ │ └── address_controller.js
│ └── application.js
├── channels
│ └── application_cable
│ │ ├── channel.rb
│ │ └── connection.rb
├── mailers
│ ├── application_mailer.rb
│ ├── message_mailer.rb
│ └── reservation_mailer.rb
├── jobs
│ ├── application_job.rb
│ └── event_job.rb
└── notifications
│ ├── guest_reservation_booked_notification.rb
│ ├── host_reservation_booked_notification.rb
│ └── new_message.rb
├── .browserslistrc
├── .node-version
├── .DS_Store
├── Procfile.dev
├── config
├── initializers
│ ├── stripe.rb
│ ├── mime_types.rb
│ ├── application_controller_renderer.rb
│ ├── cookies_serializer.rb
│ ├── filter_parameter_logging.rb
│ ├── permissions_policy.rb
│ ├── assets.rb
│ ├── wrap_parameters.rb
│ ├── backtrace_silencers.rb
│ ├── inflections.rb
│ ├── content_security_policy.rb
│ └── new_framework_defaults_7_0.rb
├── webpack
│ ├── environment.js
│ ├── test.js
│ ├── production.js
│ └── development.js
├── spring.rb
├── environment.rb
├── boot.rb
├── cable.yml
├── application.rb
├── credentials.yml.enc
├── locales
│ ├── en.yml
│ └── devise.en.yml
├── storage.yml
├── routes.rb
├── puma.rb
├── environments
│ ├── test.rb
│ ├── development.rb
│ └── production.rb
└── database.yml
├── bin
├── rake
├── rails
├── dev
├── webpack
├── webpack-dev-server
├── spring
├── yarn
├── setup
└── bundle
├── README.md
├── test
├── jobs
│ └── event_job_test.rb
├── models
│ ├── message_test.rb
│ ├── notification_test.rb
│ ├── photo_test.rb
│ ├── event_test.rb
│ ├── calendar_event_test.rb
│ └── reservation_test.rb
├── controllers
│ ├── webhooks_controller_test.rb
│ ├── host
│ │ ├── merchant_settings_controller_test.rb
│ │ ├── photos_controller_test.rb
│ │ └── reservations_controller_test.rb
│ ├── messages_controller_test.rb
│ ├── listings_controller_test.rb
│ └── reservations_controller_test.rb
├── fixtures
│ ├── messages.yml
│ ├── notifications.yml
│ ├── photos.yml
│ ├── events.yml
│ ├── reservations.yml
│ └── calendar_events.yml
└── mailers
│ ├── previews
│ ├── message_mailer_preview.rb
│ └── reservation_mailer_preview.rb
│ ├── message_mailer_test.rb
│ └── reservation_mailer_test.rb
├── db
├── migrate
│ ├── 20210926182544_add_name_to_users.rb
│ ├── 20220317141605_add_phone_number_to_users.rb
│ ├── 20210926175111_create_users.rb
│ ├── 20211127001249_add_stripe_customer_id_to_users.rb
│ ├── 20211127001336_add_stripe_product_id_to_lisitngs.rb
│ ├── 20211127013735_add_stripe_refund_id_to_reservations.rb
│ ├── 20220317151520_add_identity_verified_to_users.rb
│ ├── 20211126234514_add_price_to_listing.rb
│ ├── 20211127012331_add_stripe_payment_intent_id_to_reservations.rb
│ ├── 20211013230033_create_beds.rb
│ ├── 20211013230015_create_rooms.rb
│ ├── 20211126230105_create_photos.rb
│ ├── 20210926182417_add_omniauth_columns_to_user.rb
│ ├── 20211223203847_add_host_fields_to_users.rb
│ ├── 20211127010215_create_events.rb
│ ├── 20220315205850_create_notifications.rb
│ ├── 20211126235956_create_reservations.rb
│ ├── 20220309151733_create_calendar_events.rb
│ ├── 20220316123857_create_messages.rb
│ ├── 20210926184417_create_listings.rb
│ ├── 20210926175127_add_devise_to_users.rb
│ └── 20211126230823_create_active_storage_tables.active_storage.rb
├── seeds.rb
└── schema.rb
├── config.ru
├── Rakefile
├── postcss.config.js
├── .gitattributes
├── package.json
├── .gitignore
├── babel.config.js
├── Gemfile
└── Gemfile.lock
/log/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/storage/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tmp/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/vendor/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lib/assets/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lib/tasks/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tmp/pids/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.ruby-version:
--------------------------------------------------------------------------------
1 | 2.7.2
2 |
--------------------------------------------------------------------------------
/app/assets/builds/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/assets/images/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.browserslistrc:
--------------------------------------------------------------------------------
1 | defaults
2 |
--------------------------------------------------------------------------------
/.node-version:
--------------------------------------------------------------------------------
1 | 16.13.1
2 |
--------------------------------------------------------------------------------
/app/models/concerns/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/controllers/concerns/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/apple-touch-icon-precomposed.png:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/views/layouts/mailer.text.erb:
--------------------------------------------------------------------------------
1 | <%= yield %>
2 |
--------------------------------------------------------------------------------
/app/helpers/messages_helper.rb:
--------------------------------------------------------------------------------
1 | module MessagesHelper
2 | end
3 |
--------------------------------------------------------------------------------
/app/helpers/webhooks_helper.rb:
--------------------------------------------------------------------------------
1 | module WebhooksHelper
2 | end
3 |
--------------------------------------------------------------------------------
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cjavdev/clearbnb/HEAD/.DS_Store
--------------------------------------------------------------------------------
/Procfile.dev:
--------------------------------------------------------------------------------
1 | web: bin/rails server -p 3000
2 | js: yarn build --watch
3 |
--------------------------------------------------------------------------------
/app/helpers/host/photos_helper.rb:
--------------------------------------------------------------------------------
1 | module Host::PhotosHelper
2 | end
3 |
--------------------------------------------------------------------------------
/app/helpers/host/rooms_helper.rb:
--------------------------------------------------------------------------------
1 | module Host::RoomsHelper
2 | end
3 |
--------------------------------------------------------------------------------
/app/helpers/reservations_helper.rb:
--------------------------------------------------------------------------------
1 | module ReservationsHelper
2 | end
3 |
--------------------------------------------------------------------------------
/app/helpers/static_pages_helper.rb:
--------------------------------------------------------------------------------
1 | module StaticPagesHelper
2 | end
3 |
--------------------------------------------------------------------------------
/app/helpers/host/listings_helper.rb:
--------------------------------------------------------------------------------
1 | module Host::ListingsHelper
2 | end
3 |
--------------------------------------------------------------------------------
/app/helpers/host/reservations_helper.rb:
--------------------------------------------------------------------------------
1 | module Host::ReservationsHelper
2 | end
3 |
--------------------------------------------------------------------------------
/app/helpers/host/merchant_settings_helper.rb:
--------------------------------------------------------------------------------
1 | module Host::MerchantSettingsHelper
2 | end
3 |
--------------------------------------------------------------------------------
/app/helpers/listings_helper.rb:
--------------------------------------------------------------------------------
1 | module ListingsHelper
2 | include Pagy::Frontend
3 | end
4 |
--------------------------------------------------------------------------------
/app/helpers/users/omniauth_callbacks_helper.rb:
--------------------------------------------------------------------------------
1 | module Users::OmniauthCallbacksHelper
2 | end
3 |
--------------------------------------------------------------------------------
/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | class ApplicationController < ActionController::Base
2 | end
3 |
--------------------------------------------------------------------------------
/config/initializers/stripe.rb:
--------------------------------------------------------------------------------
1 | Stripe.api_key = Rails.application.credentials.dig(:stripe, :secret_key)
2 |
--------------------------------------------------------------------------------
/bin/rake:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require_relative "../config/boot"
3 | require "rake"
4 | Rake.application.run
5 |
--------------------------------------------------------------------------------
/app/models/application_record.rb:
--------------------------------------------------------------------------------
1 | class ApplicationRecord < ActiveRecord::Base
2 | self.abstract_class = true
3 | end
4 |
--------------------------------------------------------------------------------
/app/views/message_mailer/new_message.html.erb:
--------------------------------------------------------------------------------
1 |
New Message
2 |
3 |
4 | <%= @message.content %>
5 |
6 |
--------------------------------------------------------------------------------
/config/webpack/environment.js:
--------------------------------------------------------------------------------
1 | const { environment } = require('@rails/webpacker')
2 |
3 | module.exports = environment
4 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
2 |
--------------------------------------------------------------------------------
/app/assets/config/manifest.js:
--------------------------------------------------------------------------------
1 | //= link_tree ../images
2 | //= link_directory ../stylesheets .css
3 | //= link_tree ../builds
4 |
--------------------------------------------------------------------------------
/app/views/static_pages/home.html.erb:
--------------------------------------------------------------------------------
1 | StaticPages#home
2 | Find me in app/views/static_pages/home.html.erb
3 |
--------------------------------------------------------------------------------
/app/controllers/static_pages_controller.rb:
--------------------------------------------------------------------------------
1 | class StaticPagesController < ApplicationController
2 | def home
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/app/javascript/channels/index.js:
--------------------------------------------------------------------------------
1 | import { createConsumer } from "@rails/actioncable"
2 |
3 | export default createConsumer()
4 |
--------------------------------------------------------------------------------
/app/channels/application_cable/channel.rb:
--------------------------------------------------------------------------------
1 | module ApplicationCable
2 | class Channel < ActionCable::Channel::Base
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/config/spring.rb:
--------------------------------------------------------------------------------
1 | Spring.watch(
2 | ".ruby-version",
3 | ".rbenv-vars",
4 | "tmp/restart.txt",
5 | "tmp/caching-dev.txt"
6 | )
7 |
--------------------------------------------------------------------------------
/app/channels/application_cable/connection.rb:
--------------------------------------------------------------------------------
1 | module ApplicationCable
2 | class Connection < ActionCable::Connection::Base
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/app/views/host/merchant_settings/connected.html.erb:
--------------------------------------------------------------------------------
1 | Connected
2 |
3 | Thanks so much!
4 |
--------------------------------------------------------------------------------
/app/mailers/application_mailer.rb:
--------------------------------------------------------------------------------
1 | class ApplicationMailer < ActionMailer::Base
2 | default from: 'from@example.com'
3 | layout 'mailer'
4 | end
5 |
--------------------------------------------------------------------------------
/app/models/notification.rb:
--------------------------------------------------------------------------------
1 | class Notification < ApplicationRecord
2 | include Noticed::Model
3 | belongs_to :recipient, polymorphic: true
4 | end
5 |
--------------------------------------------------------------------------------
/app/views/message_mailer/new_message.text.erb:
--------------------------------------------------------------------------------
1 | Message#new_message
2 |
3 | <%= @greeting %>, find me in app/views/message_mailer/new_message.text.erb
4 |
--------------------------------------------------------------------------------
/app/views/shared/_errors.html.erb:
--------------------------------------------------------------------------------
1 | <% if flash[:errors] %>
2 | <% flash[:errors].each do |error| %>
3 | <%= error %>
4 | <% end %>
5 | <% end %>
6 |
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Clearbnb
2 |
3 | Want to see this thing built from scratch?
4 | [YouTube](https://www.youtube.com/playlist?list=PLS6F722u-R6LoD3UN0EE_cKtHVG2EWn0t)
5 |
--------------------------------------------------------------------------------
/app/views/reservation_mailer/guest_booked.text.erb:
--------------------------------------------------------------------------------
1 | Reservation#guest_booked
2 |
3 | <%= @greeting %>, find me in app/views/reservation_mailer/guest_booked.text.erb
4 |
--------------------------------------------------------------------------------
/app/views/reservation_mailer/host_booked.text.erb:
--------------------------------------------------------------------------------
1 | Reservation#host_booked
2 |
3 | <%= @greeting %>, find me in app/views/reservation_mailer/host_booked.text.erb
4 |
--------------------------------------------------------------------------------
/bin/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | APP_PATH = File.expand_path("../config/application", __dir__)
3 | require_relative "../config/boot"
4 | require "rails/commands"
5 |
--------------------------------------------------------------------------------
/config/environment.rb:
--------------------------------------------------------------------------------
1 | # Load the Rails application.
2 | require_relative "application"
3 |
4 | # Initialize the Rails application.
5 | Rails.application.initialize!
6 |
--------------------------------------------------------------------------------
/test/jobs/event_job_test.rb:
--------------------------------------------------------------------------------
1 | require "test_helper"
2 |
3 | class EventJobTest < ActiveJob::TestCase
4 | # test "the truth" do
5 | # assert true
6 | # end
7 | end
8 |
--------------------------------------------------------------------------------
/app/views/devise/mailer/password_change.html.erb:
--------------------------------------------------------------------------------
1 | Hello <%= @resource.email %>!
2 |
3 | We're contacting you to notify you that your password has been changed.
4 |
--------------------------------------------------------------------------------
/test/models/message_test.rb:
--------------------------------------------------------------------------------
1 | require "test_helper"
2 |
3 | class MessageTest < ActiveSupport::TestCase
4 | # test "the truth" do
5 | # assert true
6 | # end
7 | end
8 |
--------------------------------------------------------------------------------
/db/migrate/20210926182544_add_name_to_users.rb:
--------------------------------------------------------------------------------
1 | class AddNameToUsers < ActiveRecord::Migration[6.1]
2 | def change
3 | add_column :users, :name, :string
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/test/models/notification_test.rb:
--------------------------------------------------------------------------------
1 | require "test_helper"
2 |
3 | class NotificationTest < ActiveSupport::TestCase
4 | # test "the truth" do
5 | # assert true
6 | # end
7 | end
8 |
--------------------------------------------------------------------------------
/config.ru:
--------------------------------------------------------------------------------
1 | # This file is used by Rack-based servers to start the application.
2 |
3 | require_relative "config/environment"
4 |
5 | run Rails.application
6 | Rails.application.load_server
7 |
--------------------------------------------------------------------------------
/config/webpack/test.js:
--------------------------------------------------------------------------------
1 | process.env.NODE_ENV = process.env.NODE_ENV || 'development'
2 |
3 | const environment = require('./environment')
4 |
5 | module.exports = environment.toWebpackConfig()
6 |
--------------------------------------------------------------------------------
/config/webpack/production.js:
--------------------------------------------------------------------------------
1 | process.env.NODE_ENV = process.env.NODE_ENV || 'production'
2 |
3 | const environment = require('./environment')
4 |
5 | module.exports = environment.toWebpackConfig()
6 |
--------------------------------------------------------------------------------
/db/migrate/20220317141605_add_phone_number_to_users.rb:
--------------------------------------------------------------------------------
1 | class AddPhoneNumberToUsers < ActiveRecord::Migration[7.0]
2 | def change
3 | add_column :users, :phone_number, :string
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/bin/dev:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | if ! foreman version &> /dev/null
4 | then
5 | echo "Installing foreman..."
6 | gem install foreman
7 | fi
8 |
9 | foreman start -f Procfile.dev "$@"
10 |
--------------------------------------------------------------------------------
/config/initializers/mime_types.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new mime types for use in respond_to blocks:
4 | # Mime::Type.register "text/richtext", :rtf
5 |
--------------------------------------------------------------------------------
/config/webpack/development.js:
--------------------------------------------------------------------------------
1 | process.env.NODE_ENV = process.env.NODE_ENV || 'development'
2 |
3 | const environment = require('./environment')
4 |
5 | module.exports = environment.toWebpackConfig()
6 |
--------------------------------------------------------------------------------
/db/migrate/20210926175111_create_users.rb:
--------------------------------------------------------------------------------
1 | class CreateUsers < ActiveRecord::Migration[6.1]
2 | def change
3 | create_table :users do |t|
4 |
5 | t.timestamps
6 | end
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/test/controllers/webhooks_controller_test.rb:
--------------------------------------------------------------------------------
1 | require "test_helper"
2 |
3 | class WebhooksControllerTest < ActionDispatch::IntegrationTest
4 | # test "the truth" do
5 | # assert true
6 | # end
7 | end
8 |
--------------------------------------------------------------------------------
/app/controllers/host/reservations_controller.rb:
--------------------------------------------------------------------------------
1 | class Host::ReservationsController < ApplicationController
2 | def show
3 | @reservation = current_user.host_reservations.find(params[:id])
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20211127001249_add_stripe_customer_id_to_users.rb:
--------------------------------------------------------------------------------
1 | class AddStripeCustomerIdToUsers < ActiveRecord::Migration[7.0]
2 | def change
3 | add_column :users, :stripe_customer_id, :string
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20211127001336_add_stripe_product_id_to_lisitngs.rb:
--------------------------------------------------------------------------------
1 | class AddStripeProductIdToLisitngs < ActiveRecord::Migration[7.0]
2 | def change
3 | add_column :listings, :stripe_product_id, :string
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/host/listings.scss:
--------------------------------------------------------------------------------
1 | // Place all the styles related to the Host/Listings controller here.
2 | // They will automatically be included in application.css.
3 | // You can use Sass (SCSS) here: https://sass-lang.com/
4 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/static_pages.scss:
--------------------------------------------------------------------------------
1 | // Place all the styles related to the StaticPages controller here.
2 | // They will automatically be included in application.css.
3 | // You can use Sass (SCSS) here: https://sass-lang.com/
4 |
--------------------------------------------------------------------------------
/config/boot.rb:
--------------------------------------------------------------------------------
1 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
2 |
3 | require "bundler/setup" # Set up gems listed in the Gemfile.
4 | require "bootsnap/setup" # Speed up boot time by caching expensive operations.
5 |
--------------------------------------------------------------------------------
/db/migrate/20211127013735_add_stripe_refund_id_to_reservations.rb:
--------------------------------------------------------------------------------
1 | class AddStripeRefundIdToReservations < ActiveRecord::Migration[7.0]
2 | def change
3 | add_column :reservations, :stripe_refund_id, :string
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20220317151520_add_identity_verified_to_users.rb:
--------------------------------------------------------------------------------
1 | class AddIdentityVerifiedToUsers < ActiveRecord::Migration[7.0]
2 | def change
3 | add_column :users, :identity_verified, :boolean, default: false
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/test/controllers/host/merchant_settings_controller_test.rb:
--------------------------------------------------------------------------------
1 | require "test_helper"
2 |
3 | class Host::MerchantSettingsControllerTest < ActionDispatch::IntegrationTest
4 | # test "the truth" do
5 | # assert true
6 | # end
7 | end
8 |
--------------------------------------------------------------------------------
/db/migrate/20211126234514_add_price_to_listing.rb:
--------------------------------------------------------------------------------
1 | class AddPriceToListing < ActiveRecord::Migration[7.0]
2 | def change
3 | add_column :listings, :nightly_price, :integer
4 | add_column :listings, :cleaning_fee, :integer
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/config/cable.yml:
--------------------------------------------------------------------------------
1 | development:
2 | adapter: async
3 |
4 | test:
5 | adapter: test
6 |
7 | production:
8 | adapter: redis
9 | url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
10 | channel_prefix: clearbnb_production
11 |
--------------------------------------------------------------------------------
/db/migrate/20211127012331_add_stripe_payment_intent_id_to_reservations.rb:
--------------------------------------------------------------------------------
1 | class AddStripePaymentIntentIdToReservations < ActiveRecord::Migration[7.0]
2 | def change
3 | add_column :reservations, :stripe_payment_intent_id, :string
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/test/controllers/messages_controller_test.rb:
--------------------------------------------------------------------------------
1 | require "test_helper"
2 |
3 | class MessagesControllerTest < ActionDispatch::IntegrationTest
4 | test "should get index" do
5 | get messages_index_url
6 | assert_response :success
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/users/omniauth_callbacks.scss:
--------------------------------------------------------------------------------
1 | // Place all the styles related to the Users/OmniauthCallbacks controller here.
2 | // They will automatically be included in application.css.
3 | // You can use Sass (SCSS) here: https://sass-lang.com/
4 |
--------------------------------------------------------------------------------
/test/controllers/host/photos_controller_test.rb:
--------------------------------------------------------------------------------
1 | require "test_helper"
2 |
3 | class Host::PhotosControllerTest < ActionDispatch::IntegrationTest
4 | test "should get index" do
5 | get host_photos_index_url
6 | assert_response :success
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/app/views/reservation_mailer/guest_booked.html.erb:
--------------------------------------------------------------------------------
1 | Booking confirmed!
2 |
3 |
4 | You're all set to stay at <%= @reservation.listing.title %>
5 |
6 |
7 |
8 | <%= @reservation.start_date %> to <%= @reservation.end_date %>
9 |
10 |
--------------------------------------------------------------------------------
/app/views/devise/mailer/confirmation_instructions.html.erb:
--------------------------------------------------------------------------------
1 | Welcome <%= @email %>!
2 |
3 | You can confirm your account email through the link below:
4 |
5 | <%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %>
6 |
--------------------------------------------------------------------------------
/app/views/reservation_mailer/host_booked.html.erb:
--------------------------------------------------------------------------------
1 | New Reservation
2 |
3 |
4 | <%= @reservation.guest.email %> booked your property "<%= @reservation.listing.title %>"
5 |
6 |
7 | <%= @reservation.start_date %> to <%= @reservation.end_date %>
8 |
9 |
--------------------------------------------------------------------------------
/test/controllers/host/reservations_controller_test.rb:
--------------------------------------------------------------------------------
1 | require "test_helper"
2 |
3 | class Host::ReservationsControllerTest < ActionDispatch::IntegrationTest
4 | test "should get show" do
5 | get host_reservations_show_url
6 | assert_response :success
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | # Add your own tasks in files placed in lib/tasks ending in .rake,
2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3 |
4 | require_relative "config/application"
5 | require 'resque/tasks'
6 |
7 | Rails.application.load_tasks
8 |
--------------------------------------------------------------------------------
/app/views/shared/_notices.html.erb:
--------------------------------------------------------------------------------
1 | <% if notice %>
2 | <%= notice %>
3 | <% end %>
4 | <% if alert %>
5 | <%= alert %>
6 | <% end %>
7 |
--------------------------------------------------------------------------------
/config/initializers/application_controller_renderer.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # ActiveSupport::Reloader.to_prepare do
4 | # ApplicationController.renderer.defaults.merge!(
5 | # http_host: 'example.org',
6 | # https: false
7 | # )
8 | # end
9 |
--------------------------------------------------------------------------------
/db/migrate/20211013230033_create_beds.rb:
--------------------------------------------------------------------------------
1 | class CreateBeds < ActiveRecord::Migration[6.1]
2 | def change
3 | create_table :beds do |t|
4 | t.references :room, null: false, foreign_key: true
5 | t.integer :bed_size
6 |
7 | t.timestamps
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: [
3 | require('postcss-import'),
4 | require('postcss-flexbugs-fixes'),
5 | require('postcss-preset-env')({
6 | autoprefixer: {
7 | flexbox: 'no-2009'
8 | },
9 | stage: 3
10 | })
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/config/initializers/cookies_serializer.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Specify a serializer for the signed and encrypted cookie jars.
4 | # Valid options are :json, :marshal, and :hybrid.
5 | Rails.application.config.action_dispatch.cookies_serializer = :json
6 |
--------------------------------------------------------------------------------
/db/migrate/20211013230015_create_rooms.rb:
--------------------------------------------------------------------------------
1 | class CreateRooms < ActiveRecord::Migration[6.1]
2 | def change
3 | create_table :rooms do |t|
4 | t.references :listing, null: false, foreign_key: true
5 | t.integer :room_type
6 |
7 | t.timestamps
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/db/migrate/20211126230105_create_photos.rb:
--------------------------------------------------------------------------------
1 | class CreatePhotos < ActiveRecord::Migration[7.0]
2 | def change
3 | create_table :photos do |t|
4 | t.references :listing, null: false, foreign_key: true
5 | t.string :caption
6 |
7 | t.timestamps
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/app/controllers/listings_controller.rb:
--------------------------------------------------------------------------------
1 | class ListingsController < ApplicationController
2 | include Pagy::Backend
3 |
4 | def index
5 | @pagy, @listings = pagy(Listing.published, items: 6)
6 | end
7 |
8 | def show
9 | @listing = Listing.published.find(params[:id])
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/app/jobs/application_job.rb:
--------------------------------------------------------------------------------
1 | class ApplicationJob < ActiveJob::Base
2 | # Automatically retry jobs that encountered a deadlock
3 | # retry_on ActiveRecord::Deadlocked
4 |
5 | # Most jobs are safe to ignore if the underlying records are no longer available
6 | # discard_on ActiveJob::DeserializationError
7 | end
8 |
--------------------------------------------------------------------------------
/app/views/layouts/mailer.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
10 |
11 | <%= yield %>
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/javascript/channels/consumer.js:
--------------------------------------------------------------------------------
1 | // Action Cable provides the framework to deal with WebSockets in Rails.
2 | // You can generate new channels where WebSocket features live using the `bin/rails generate channel` command.
3 |
4 | import { createConsumer } from "@rails/actioncable"
5 |
6 | export default createConsumer()
7 |
--------------------------------------------------------------------------------
/config/initializers/filter_parameter_logging.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Configure sensitive parameters which will be filtered from the log file.
4 | Rails.application.config.filter_parameters += [
5 | :passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn
6 | ]
7 |
--------------------------------------------------------------------------------
/db/migrate/20210926182417_add_omniauth_columns_to_user.rb:
--------------------------------------------------------------------------------
1 | class AddOmniauthColumnsToUser < ActiveRecord::Migration[6.1]
2 | def change
3 | add_column :users, :provider, :string
4 | add_column :users, :uid, :string
5 |
6 | add_index :users, :uid
7 | add_index :users, [:provider, :uid]
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/db/migrate/20211223203847_add_host_fields_to_users.rb:
--------------------------------------------------------------------------------
1 | class AddHostFieldsToUsers < ActiveRecord::Migration[7.0]
2 | def change
3 | add_column :users, :is_host, :boolean, default: false
4 | add_column :users, :stripe_account_id, :string
5 | add_column :users, :charges_enabled, :boolean, default: false
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/test/fixtures/messages.yml:
--------------------------------------------------------------------------------
1 | # Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
2 |
3 | one:
4 | from_user: one
5 | to_user: one
6 | reservation: one
7 | content: MyString
8 |
9 | two:
10 | from_user: two
11 | to_user: two
12 | reservation: two
13 | content: MyString
14 |
--------------------------------------------------------------------------------
/app/javascript/controllers/application.js:
--------------------------------------------------------------------------------
1 | import { Application } from "@hotwired/stimulus"
2 |
3 | const application = Application.start()
4 |
5 | // Configure Stimulus development experience
6 | application.warnings = true
7 | application.debug = false
8 | window.Stimulus = application
9 |
10 | export { application }
11 |
--------------------------------------------------------------------------------
/app/views/devise/mailer/unlock_instructions.html.erb:
--------------------------------------------------------------------------------
1 | Hello <%= @resource.email %>!
2 |
3 | Your account has been locked due to an excessive number of unsuccessful sign in attempts.
4 |
5 | Click the link below to unlock your account:
6 |
7 | <%= link_to 'Unlock my account', unlock_url(@resource, unlock_token: @token) %>
8 |
--------------------------------------------------------------------------------
/test/mailers/previews/message_mailer_preview.rb:
--------------------------------------------------------------------------------
1 | # Preview all emails at http://localhost:3000/rails/mailers/message_mailer
2 | class MessageMailerPreview < ActionMailer::Preview
3 |
4 | # Preview this email at http://localhost:3000/rails/mailers/message_mailer/new_message
5 | def new_message
6 | MessageMailer.new_message
7 | end
8 |
9 | end
10 |
--------------------------------------------------------------------------------
/app/views/host/listings/show.html.erb:
--------------------------------------------------------------------------------
1 | <%= @listing.title %>
2 |
3 | <%= link_to "Edit details", edit_host_listing_path(@listing) %>
4 | <%= link_to "Edit rooms", host_listing_rooms_path(@listing) %>
5 | <%= link_to "Edit photos", host_listing_photos_path(@listing) %>
6 |
7 | <%= JSON.pretty_generate(@listing.as_json) %>
8 |
--------------------------------------------------------------------------------
/db/migrate/20211127010215_create_events.rb:
--------------------------------------------------------------------------------
1 | class CreateEvents < ActiveRecord::Migration[7.0]
2 | def change
3 | create_table :events do |t|
4 | t.text :request_body
5 | t.integer :status, default: 0
6 | t.text :error_message
7 | t.string :source
8 | t.string :event_type
9 |
10 | t.timestamps
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/test/controllers/listings_controller_test.rb:
--------------------------------------------------------------------------------
1 | require "test_helper"
2 |
3 | class ListingsControllerTest < ActionDispatch::IntegrationTest
4 | test "should get index" do
5 | get listings_index_url
6 | assert_response :success
7 | end
8 |
9 | test "should get show" do
10 | get listings_show_url
11 | assert_response :success
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/app/views/devise/mailer/email_changed.html.erb:
--------------------------------------------------------------------------------
1 | Hello <%= @email %>!
2 |
3 | <% if @resource.try(:unconfirmed_email?) %>
4 | We're contacting you to notify you that your email is being changed to <%= @resource.unconfirmed_email %>.
5 | <% else %>
6 | We're contacting you to notify you that your email has been changed to <%= @resource.email %>.
7 | <% end %>
8 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # See https://git-scm.com/docs/gitattributes for more about git attribute files.
2 |
3 | # Mark the database schema as having been generated.
4 | db/schema.rb linguist-generated
5 |
6 | # Mark the yarn lockfile as having been generated.
7 | yarn.lock linguist-generated
8 |
9 | # Mark any vendored files as having been vendored.
10 | vendor/* linguist-vendored
11 |
--------------------------------------------------------------------------------
/app/controllers/webhooks_controller.rb:
--------------------------------------------------------------------------------
1 | class WebhooksController < ApplicationController
2 | skip_before_action :verify_authenticity_token
3 |
4 | def create
5 | event = Event.create!(
6 | request_body: request.raw_post,
7 | source: params[:source],
8 | )
9 | EventJob.perform_later(event)
10 | render json: { message: "success" }
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/test/controllers/reservations_controller_test.rb:
--------------------------------------------------------------------------------
1 | require "test_helper"
2 |
3 | class ReservationsControllerTest < ActionDispatch::IntegrationTest
4 | test "should get index" do
5 | get reservations_index_url
6 | assert_response :success
7 | end
8 |
9 | test "should get show" do
10 | get reservations_show_url
11 | assert_response :success
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/test/fixtures/notifications.yml:
--------------------------------------------------------------------------------
1 | # Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
2 |
3 | one:
4 | recipient: one
5 | recipient_type: Recipient
6 | type:
7 | params:
8 | read_at: 2022-03-15 16:58:50
9 |
10 | two:
11 | recipient: two
12 | recipient_type: Recipient
13 | type:
14 | params:
15 | read_at: 2022-03-15 16:58:50
16 |
--------------------------------------------------------------------------------
/test/mailers/message_mailer_test.rb:
--------------------------------------------------------------------------------
1 | require "test_helper"
2 |
3 | class MessageMailerTest < ActionMailer::TestCase
4 | test "new_message" do
5 | mail = MessageMailer.new_message
6 | assert_equal "New message", mail.subject
7 | assert_equal ["to@example.org"], mail.to
8 | assert_equal ["from@example.com"], mail.from
9 | assert_match "Hi", mail.body.encoded
10 | end
11 |
12 | end
13 |
--------------------------------------------------------------------------------
/app/models/photo.rb:
--------------------------------------------------------------------------------
1 | # == Schema Information
2 | #
3 | # Table name: photos
4 | #
5 | # id :bigint not null, primary key
6 | # listing_id :bigint not null
7 | # caption :string
8 | # created_at :datetime not null
9 | # updated_at :datetime not null
10 | #
11 | class Photo < ApplicationRecord
12 | belongs_to :listing
13 | has_one_attached :photo
14 | end
15 |
--------------------------------------------------------------------------------
/app/mailers/message_mailer.rb:
--------------------------------------------------------------------------------
1 | class MessageMailer < ApplicationMailer
2 |
3 | # Subject can be set in your I18n file at config/locales/en.yml
4 | # with the following lookup:
5 | #
6 | # en.message_mailer.new_message.subject
7 | #
8 | def new_message
9 | @message = params[:message]
10 |
11 | mail to: @message.to_user.email, subject: "New message from #{@message.from_user.email}"
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/db/migrate/20220315205850_create_notifications.rb:
--------------------------------------------------------------------------------
1 | class CreateNotifications < ActiveRecord::Migration[7.0]
2 | def change
3 | create_table :notifications do |t|
4 | t.references :recipient, polymorphic: true, null: false
5 | t.string :type, null: false
6 | t.jsonb :params
7 | t.datetime :read_at
8 |
9 | t.timestamps
10 | end
11 | add_index :notifications, :read_at
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/db/migrate/20211126235956_create_reservations.rb:
--------------------------------------------------------------------------------
1 | class CreateReservations < ActiveRecord::Migration[7.0]
2 | def change
3 | create_table :reservations do |t|
4 | t.references :listing, null: false, foreign_key: true
5 | t.references :guest, null: false, foreign_key: { to_table: :users }
6 | t.string :session_id
7 | t.integer :status, default: 0
8 |
9 | t.timestamps
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/test/fixtures/photos.yml:
--------------------------------------------------------------------------------
1 | # == Schema Information
2 | #
3 | # Table name: photos
4 | #
5 | # id :bigint not null, primary key
6 | # listing_id :bigint not null
7 | # caption :string
8 | # created_at :datetime not null
9 | # updated_at :datetime not null
10 | #
11 |
12 | one:
13 | photo: MyString
14 | caption: MyString
15 |
16 | two:
17 | photo: MyString
18 | caption: MyString
19 |
--------------------------------------------------------------------------------
/config/initializers/permissions_policy.rb:
--------------------------------------------------------------------------------
1 | # Define an application-wide HTTP permissions policy. For further
2 | # information see https://developers.google.com/web/updates/2018/06/feature-policy
3 | #
4 | # Rails.application.config.permissions_policy do |f|
5 | # f.camera :none
6 | # f.gyroscope :none
7 | # f.microphone :none
8 | # f.usb :none
9 | # f.fullscreen :self
10 | # f.payment :self, "https://secure.example.com"
11 | # end
12 |
--------------------------------------------------------------------------------
/test/models/photo_test.rb:
--------------------------------------------------------------------------------
1 | # == Schema Information
2 | #
3 | # Table name: photos
4 | #
5 | # id :bigint not null, primary key
6 | # listing_id :bigint not null
7 | # caption :string
8 | # created_at :datetime not null
9 | # updated_at :datetime not null
10 | #
11 | require "test_helper"
12 |
13 | class PhotoTest < ActiveSupport::TestCase
14 | # test "the truth" do
15 | # assert true
16 | # end
17 | end
18 |
--------------------------------------------------------------------------------
/app/views/devise/mailer/reset_password_instructions.html.erb:
--------------------------------------------------------------------------------
1 | Hello <%= @resource.email %>!
2 |
3 | Someone has requested a link to change your password. You can do this through the link below.
4 |
5 | <%= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token) %>
6 |
7 | If you didn't request this, please ignore this email.
8 | Your password won't change until you access the link above and create a new one.
9 |
--------------------------------------------------------------------------------
/app/javascript/controllers/room_controller.js:
--------------------------------------------------------------------------------
1 | import { Controller } from "@hotwired/stimulus"
2 |
3 | // Connects to data-controller="room"
4 | export default class extends Controller {
5 | static targets = [
6 | 'bedFields',
7 | 'beds',
8 | ]
9 |
10 | connect() {
11 | }
12 |
13 | addBed(e) {
14 | e.preventDefault();
15 | this.bedsTarget.insertAdjacentHTML(
16 | 'beforeend',
17 | this.bedFieldsTarget.innerHTML
18 | )
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/db/migrate/20220309151733_create_calendar_events.rb:
--------------------------------------------------------------------------------
1 | class CreateCalendarEvents < ActiveRecord::Migration[7.0]
2 | def change
3 | create_table :calendar_events do |t|
4 | t.references :listing, null: false, foreign_key: true
5 | t.references :reservation, null: true, foreign_key: true
6 | t.integer :status, null: false
7 | t.date :start_date, null: false
8 | t.date :end_date, null: false
9 |
10 | t.timestamps
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/app/javascript/application.js:
--------------------------------------------------------------------------------
1 | // Entry point for the build script in your package.json
2 | // import * as ActiveStorage from "@rails/activestorage"
3 | import "@hotwired/turbo-rails"
4 | import "./channels"
5 | import "./controllers"
6 |
7 | // ActiveStorage.start()
8 | console.log("here")
9 | window.initMap = () => {
10 | console.log('initMap was called');
11 | const event = new Event("MapLoaded")
12 | event.initEvent("map-loaded", true, true);
13 | window.dispatchEvent(event)
14 | }
15 |
--------------------------------------------------------------------------------
/db/migrate/20220316123857_create_messages.rb:
--------------------------------------------------------------------------------
1 | class CreateMessages < ActiveRecord::Migration[7.0]
2 | def change
3 | create_table :messages do |t|
4 | t.references :from_user, null: false, foreign_key: { to_table: :users }
5 | t.references :to_user, null: false, foreign_key: { to_table: :users }
6 | t.references :reservation, null: true, foreign_key: true
7 | t.string :content, null: false, default: ''
8 |
9 | t.timestamps
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/bin/webpack:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
4 | ENV["NODE_ENV"] ||= "development"
5 |
6 | require "pathname"
7 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
8 | Pathname.new(__FILE__).realpath)
9 |
10 | require "bundler/setup"
11 |
12 | require "webpacker"
13 | require "webpacker/webpack_runner"
14 |
15 | APP_ROOT = File.expand_path("..", __dir__)
16 | Dir.chdir(APP_ROOT) do
17 | Webpacker::WebpackRunner.run(ARGV)
18 | end
19 |
--------------------------------------------------------------------------------
/app/models/message.rb:
--------------------------------------------------------------------------------
1 | class Message < ApplicationRecord
2 | belongs_to :from_user, class_name: 'User'
3 | belongs_to :to_user, class_name: 'User'
4 | belongs_to :reservation, optional: true
5 | has_noticed_notifications
6 |
7 | after_create_commit :notify_to_user
8 |
9 | def self.mark_as_read!
10 | all.map(&:notifications_as_message).flatten.each(&:mark_as_read!)
11 | end
12 |
13 | def notify_to_user
14 | NewMessage.with(message: self).deliver_later(to_user)
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/app/views/devise/shared/_error_messages.html.erb:
--------------------------------------------------------------------------------
1 | <% if resource.errors.any? %>
2 |
3 |
4 | <%= I18n.t("errors.messages.not_saved",
5 | count: resource.errors.count,
6 | resource: resource.class.model_name.human.downcase)
7 | %>
8 |
9 |
10 | <% resource.errors.full_messages.each do |message| %>
11 | <%= message %>
12 | <% end %>
13 |
14 |
15 | <% end %>
16 |
--------------------------------------------------------------------------------
/bin/webpack-dev-server:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
4 | ENV["NODE_ENV"] ||= "development"
5 |
6 | require "pathname"
7 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
8 | Pathname.new(__FILE__).realpath)
9 |
10 | require "bundler/setup"
11 |
12 | require "webpacker"
13 | require "webpacker/dev_server_runner"
14 |
15 | APP_ROOT = File.expand_path("..", __dir__)
16 | Dir.chdir(APP_ROOT) do
17 | Webpacker::DevServerRunner.run(ARGV)
18 | end
19 |
--------------------------------------------------------------------------------
/app/models/event.rb:
--------------------------------------------------------------------------------
1 | # == Schema Information
2 | #
3 | # Table name: events
4 | #
5 | # id :bigint not null, primary key
6 | # request_body :text
7 | # status :integer default("pending")
8 | # error_message :text
9 | # source :string
10 | # event_type :string
11 | # created_at :datetime not null
12 | # updated_at :datetime not null
13 | #
14 | class Event < ApplicationRecord
15 | enum status: {pending: 0, processed: 1, failed: 2}
16 | end
17 |
--------------------------------------------------------------------------------
/app/views/reservations/show.html.erb:
--------------------------------------------------------------------------------
1 | <%= @reservation.listing.title %>
2 |
3 | <%= JSON.pretty_generate(@reservation.as_json(include: :calendar_event)) %>
4 |
5 | <%= link_to "Messages", messages_path(reservation_id: @reservation.id) %>
6 |
7 |
11 |
--------------------------------------------------------------------------------
/app/javascript/controllers/menu_controller.js:
--------------------------------------------------------------------------------
1 | import { Controller } from "@hotwired/stimulus"
2 | import {toggle} from "el-transition";
3 |
4 | // Connects to data-controller="menu"
5 | export default class extends Controller {
6 | static targets = [ "mobileMenu", "desktopMenu" ]
7 | connect() {
8 | console.log("menu controller connected");
9 | }
10 |
11 | toggleDesktopMenu() {
12 | toggle(this.desktopMenuTarget);
13 | }
14 |
15 | toggleMobileMenu() {
16 | toggle(this.mobileMenuTarget);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/models/bed.rb:
--------------------------------------------------------------------------------
1 | # == Schema Information
2 | #
3 | # Table name: beds
4 | #
5 | # id :bigint not null, primary key
6 | # room_id :bigint not null
7 | # bed_size :integer
8 | # created_at :datetime not null
9 | # updated_at :datetime not null
10 | #
11 | class Bed < ApplicationRecord
12 | belongs_to :room
13 |
14 | enum bed_size: {
15 | twin: 0,
16 | twin_xl: 1,
17 | full: 2,
18 | queen: 3,
19 | king: 4,
20 | california_king: 5,
21 | }
22 | end
23 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "clearbnb",
3 | "private": true,
4 | "dependencies": {
5 | "@hotwired/stimulus": "^3.0.1",
6 | "@hotwired/turbo-rails": "^7.1.3",
7 | "@rails/actioncable": "^7.0.0",
8 | "@rails/activestorage": "^7.0.0",
9 | "el-transition": "^0.0.7",
10 | "esbuild": "^0.14.49",
11 | "yarn": "^1.22.17"
12 | },
13 | "version": "0.1.0",
14 | "scripts": {
15 | "build": "esbuild app/javascript/*.* --bundle --sourcemap --outdir=app/assets/builds --public-path=assets"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/views/host/photos/index.html.erb:
--------------------------------------------------------------------------------
1 | Photos
2 |
3 |
4 |
13 |
--------------------------------------------------------------------------------
/test/mailers/previews/reservation_mailer_preview.rb:
--------------------------------------------------------------------------------
1 | # Preview all emails at http://localhost:3000/rails/mailers/reservation_mailer
2 | class ReservationMailerPreview < ActionMailer::Preview
3 |
4 | # Preview this email at http://localhost:3000/rails/mailers/reservation_mailer/host_booked
5 | def host_booked
6 | ReservationMailer.host_booked
7 | end
8 |
9 | # Preview this email at http://localhost:3000/rails/mailers/reservation_mailer/guest_booked
10 | def guest_booked
11 | ReservationMailer.guest_booked
12 | end
13 |
14 | end
15 |
--------------------------------------------------------------------------------
/bin/spring:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | if !defined?(Spring) && [nil, "development", "test"].include?(ENV["RAILS_ENV"])
3 | gem "bundler"
4 | require "bundler"
5 |
6 | # Load Spring without loading other gems in the Gemfile, for speed.
7 | Bundler.locked_gems&.specs&.find { |spec| spec.name == "spring" }&.tap do |spring|
8 | Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path
9 | gem "spring", spring.version
10 | require "spring/binstub"
11 | rescue Gem::LoadError
12 | # Ignore when Spring is not installed.
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/app/views/host/merchant_settings/index.html.erb:
--------------------------------------------------------------------------------
1 | Merchant Settings
2 |
3 | <% if current_user.stripe_account_id.blank? %>
4 |
5 | You have not connected your Stripe account yet.
6 |
7 |
8 | Connect your Stripe account
9 |
10 | <% else %>
11 | Stripe Account: <%= current_user.stripe_account_id %>
12 |
13 | Charges enable: <%= current_user.charges_enabled? ? 'Yes' : 'No' %>
14 | <% end %>
15 |
--------------------------------------------------------------------------------
/config/initializers/assets.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Version of your assets, change this if you want to expire all your assets.
4 | Rails.application.config.assets.version = "1.0"
5 |
6 | # Add additional assets to the asset load path.
7 | # Rails.application.config.assets.paths << Emoji.images_path
8 |
9 | # Precompile additional assets.
10 | # application.js, application.css, and all non-JS/CSS in the app/assets
11 | # folder are already added.
12 | # Rails.application.config.assets.precompile += %w( admin.js admin.css )
13 |
--------------------------------------------------------------------------------
/test/models/event_test.rb:
--------------------------------------------------------------------------------
1 | # == Schema Information
2 | #
3 | # Table name: events
4 | #
5 | # id :bigint not null, primary key
6 | # request_body :text
7 | # status :integer default("pending")
8 | # error_message :text
9 | # source :string
10 | # event_type :string
11 | # created_at :datetime not null
12 | # updated_at :datetime not null
13 | #
14 | require "test_helper"
15 |
16 | class EventTest < ActiveSupport::TestCase
17 | # test "the truth" do
18 | # assert true
19 | # end
20 | end
21 |
--------------------------------------------------------------------------------
/app/views/devise/unlocks/new.html.erb:
--------------------------------------------------------------------------------
1 | Resend unlock instructions
2 |
3 | <%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %>
4 | <%= render "devise/shared/error_messages", resource: resource %>
5 |
6 |
7 | <%= f.label :email %>
8 | <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
9 |
10 |
11 |
12 | <%= f.submit "Resend unlock instructions" %>
13 |
14 | <% end %>
15 |
16 | <%= render "devise/shared/links" %>
17 |
--------------------------------------------------------------------------------
/config/initializers/wrap_parameters.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # This file contains settings for ActionController::ParamsWrapper which
4 | # is enabled by default.
5 |
6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
7 | ActiveSupport.on_load(:action_controller) do
8 | wrap_parameters format: [:json]
9 | end
10 |
11 | # To enable root element in JSON for ActiveRecord objects.
12 | # ActiveSupport.on_load(:active_record) do
13 | # self.include_root_in_json = true
14 | # end
15 |
--------------------------------------------------------------------------------
/app/views/devise/passwords/new.html.erb:
--------------------------------------------------------------------------------
1 | Forgot your password?
2 |
3 | <%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| %>
4 | <%= render "devise/shared/error_messages", resource: resource %>
5 |
6 |
7 | <%= f.label :email %>
8 | <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
9 |
10 |
11 |
12 | <%= f.submit "Send me reset password instructions" %>
13 |
14 | <% end %>
15 |
16 | <%= render "devise/shared/links" %>
17 |
--------------------------------------------------------------------------------
/app/models/room.rb:
--------------------------------------------------------------------------------
1 | # == Schema Information
2 | #
3 | # Table name: rooms
4 | #
5 | # id :bigint not null, primary key
6 | # listing_id :bigint not null
7 | # room_type :integer
8 | # created_at :datetime not null
9 | # updated_at :datetime not null
10 | #
11 | class Room < ApplicationRecord
12 | belongs_to :listing
13 | has_many :beds, dependent: :destroy
14 | accepts_nested_attributes_for :beds
15 |
16 | enum room_type: {
17 | bedroom: 0,
18 | primary_bedroom: 1,
19 | living_room: 2,
20 | basement: 3,
21 | }
22 | end
23 |
--------------------------------------------------------------------------------
/config/initializers/backtrace_silencers.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
4 | # Rails.backtrace_cleaner.add_silencer { |line| /my_noisy_library/.match?(line) }
5 |
6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code
7 | # by setting BACKTRACE=1 before calling your invocation, like "BACKTRACE=1 ./bin/rails runner 'MyClass.perform'".
8 | Rails.backtrace_cleaner.remove_silencers! if ENV["BACKTRACE"]
9 |
--------------------------------------------------------------------------------
/app/views/host/reservations/show.html.erb:
--------------------------------------------------------------------------------
1 | Reservation
2 |
3 | <%= @reservation.listing.title %>
4 |
5 | <%= @reservation.guest.email %>
6 |
7 | <%= JSON.pretty_generate(@reservation.as_json(include: :calendar_event)) %>
8 |
9 | <%= link_to "Messages", messages_path(reservation_id: @reservation.id) %>
10 |
11 |
15 |
--------------------------------------------------------------------------------
/bin/yarn:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | APP_ROOT = File.expand_path('..', __dir__)
3 | Dir.chdir(APP_ROOT) do
4 | yarn = ENV["PATH"].split(File::PATH_SEPARATOR).
5 | select { |dir| File.expand_path(dir) != __dir__ }.
6 | product(["yarn", "yarn.cmd", "yarn.ps1"]).
7 | map { |dir, file| File.expand_path(file, dir) }.
8 | find { |file| File.executable?(file) }
9 |
10 | if yarn
11 | exec yarn, *ARGV
12 | else
13 | $stderr.puts "Yarn executable was not detected in the system."
14 | $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install"
15 | exit 1
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/test/models/calendar_event_test.rb:
--------------------------------------------------------------------------------
1 | # == Schema Information
2 | #
3 | # Table name: calendar_events
4 | #
5 | # id :bigint not null, primary key
6 | # listing_id :bigint not null
7 | # reservation_id :bigint
8 | # status :integer not null
9 | # start_date :date not null
10 | # end_date :date not null
11 | # created_at :datetime not null
12 | # updated_at :datetime not null
13 | #
14 | require "test_helper"
15 |
16 | class CalendarEventTest < ActiveSupport::TestCase
17 | # test "the truth" do
18 | # assert true
19 | # end
20 | end
21 |
--------------------------------------------------------------------------------
/app/controllers/users/omniauth_callbacks_controller.rb:
--------------------------------------------------------------------------------
1 | class Users::OmniauthCallbacksController < ApplicationController
2 | def google
3 | @user = User.from_omniauth(request.env["omniauth.auth"])
4 |
5 | if @user.persisted?
6 | sign_in_and_redirect @user, event: :authentication # this will throw if @user is not activated
7 | set_flash_message(:notice, :success, kind: "Google") if is_navigational_format?
8 | else
9 | session["devise.google_data"] = request.env["omniauth.auth"].except(:extra)
10 | redirect_to new_user_registration_url
11 | end
12 | end
13 |
14 | def failure
15 | redirect_to root_path
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/app/javascript/controllers/index.js:
--------------------------------------------------------------------------------
1 | // This file is auto-generated by ./bin/rails stimulus:manifest:update
2 | // Run that command whenever you add a new controller
3 |
4 | import { application } from "./application"
5 |
6 | import AddressController from "./address_controller.js"
7 | application.register("address", AddressController)
8 |
9 | import ListingMapController from "./listing_map_controller.js"
10 | application.register("listing-map", ListingMapController)
11 |
12 | import MenuController from "./menu_controller.js"
13 | application.register("menu", MenuController)
14 |
15 | import RoomController from "./room_controller.js"
16 | application.register("room", RoomController)
17 |
--------------------------------------------------------------------------------
/app/controllers/host/photos_controller.rb:
--------------------------------------------------------------------------------
1 | class Host::PhotosController < ApplicationController
2 | before_action :authenticate_user!
3 |
4 | def index
5 | @listing = current_user.listings.find(params[:listing_id])
6 | @photos = @listing.photos
7 | end
8 |
9 | def create
10 | @listing = current_user.listings.find(params[:listing_id])
11 | @photo = @listing.photos.new(photo_params)
12 | if !@photo.save
13 | flash[:errors] = @photo.errors.full_messages
14 | end
15 | redirect_to host_listing_photos_path(@listing)
16 | end
17 |
18 | private
19 |
20 | def photo_params
21 | params.require(:photo).permit(:caption, :photo)
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/app/views/devise/confirmations/new.html.erb:
--------------------------------------------------------------------------------
1 | Resend confirmation instructions
2 |
3 | <%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %>
4 | <%= render "devise/shared/error_messages", resource: resource %>
5 |
6 |
7 | <%= f.label :email %>
8 | <%= f.email_field :email, autofocus: true, autocomplete: "email", value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email) %>
9 |
10 |
11 |
12 | <%= f.submit "Resend confirmation instructions" %>
13 |
14 | <% end %>
15 |
16 | <%= render "devise/shared/links" %>
17 |
--------------------------------------------------------------------------------
/test/fixtures/events.yml:
--------------------------------------------------------------------------------
1 | # == Schema Information
2 | #
3 | # Table name: events
4 | #
5 | # id :bigint not null, primary key
6 | # request_body :text
7 | # status :integer default("pending")
8 | # error_message :text
9 | # source :string
10 | # event_type :string
11 | # created_at :datetime not null
12 | # updated_at :datetime not null
13 | #
14 |
15 | one:
16 | request_body: MyText
17 | status: 1
18 | error_message: MyText
19 | source: MyString
20 | event_type: MyString
21 |
22 | two:
23 | request_body: MyText
24 | status: 1
25 | error_message: MyText
26 | source: MyString
27 | event_type: MyString
28 |
--------------------------------------------------------------------------------
/test/mailers/reservation_mailer_test.rb:
--------------------------------------------------------------------------------
1 | require "test_helper"
2 |
3 | class ReservationMailerTest < ActionMailer::TestCase
4 | test "host_booked" do
5 | mail = ReservationMailer.host_booked
6 | assert_equal "Host booked", mail.subject
7 | assert_equal ["to@example.org"], mail.to
8 | assert_equal ["from@example.com"], mail.from
9 | assert_match "Hi", mail.body.encoded
10 | end
11 |
12 | test "guest_booked" do
13 | mail = ReservationMailer.guest_booked
14 | assert_equal "Guest booked", mail.subject
15 | assert_equal ["to@example.org"], mail.to
16 | assert_equal ["from@example.com"], mail.from
17 | assert_match "Hi", mail.body.encoded
18 | end
19 |
20 | end
21 |
--------------------------------------------------------------------------------
/db/migrate/20210926184417_create_listings.rb:
--------------------------------------------------------------------------------
1 | class CreateListings < ActiveRecord::Migration[6.1]
2 | def change
3 | create_table :listings do |t|
4 | t.references :host, null: false, foreign_key: { to_table: :users }
5 | t.string :title, null: false
6 | t.text :about
7 | t.integer :max_guests, default: 1
8 | t.string :address_line1
9 | t.string :address_line2
10 | t.string :city
11 | t.string :state
12 | t.string :postal_code
13 | t.string :country
14 | t.decimal :lat, precision: 10, scale: 6
15 | t.decimal :lng, precision: 10, scale: 6
16 | t.integer :status, default: 0
17 |
18 | t.timestamps
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/test/fixtures/reservations.yml:
--------------------------------------------------------------------------------
1 | # == Schema Information
2 | #
3 | # Table name: reservations
4 | #
5 | # id :bigint not null, primary key
6 | # listing_id :bigint not null
7 | # guest_id :bigint not null
8 | # session_id :string
9 | # status :integer default("pending")
10 | # created_at :datetime not null
11 | # updated_at :datetime not null
12 | # stripe_payment_intent_id :string
13 | # stripe_refund_id :string
14 | #
15 |
16 | one:
17 | session_id: MyString
18 | status: 1
19 |
20 | two:
21 | session_id: MyString
22 | status: 1
23 |
--------------------------------------------------------------------------------
/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | module ApplicationHelper
2 | def navigation_items
3 | [
4 | { body: 'Listings', href: listings_path },
5 | { body: 'Reservations', href: reservations_path },
6 | { body: 'My Listings', href: host_listings_path },
7 | ]
8 | end
9 |
10 | def navigation_class(path)
11 | if current_page?(path)
12 | "bg-gray-900 text-white px-3 py-2 rounded-md text-sm font-medium"
13 | else
14 | "text-gray-300 hover:bg-gray-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium"
15 | end
16 | end
17 |
18 | def navigation_aria(path)
19 | if current_page?(path)
20 | "page"
21 | else
22 | "false"
23 | end
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new inflection rules using the following format. Inflections
4 | # are locale specific, and you may define rules for as many different
5 | # locales as you wish. All of these examples are active by default:
6 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
7 | # inflect.plural /^(ox)$/i, "\\1en"
8 | # inflect.singular /^(ox)en/i, "\\1"
9 | # inflect.irregular "person", "people"
10 | # inflect.uncountable %w( fish sheep )
11 | # end
12 |
13 | # These inflection rules are supported but not enabled by default:
14 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
15 | # inflect.acronym "RESTful"
16 | # end
17 |
--------------------------------------------------------------------------------
/test/fixtures/calendar_events.yml:
--------------------------------------------------------------------------------
1 | # == Schema Information
2 | #
3 | # Table name: calendar_events
4 | #
5 | # id :bigint not null, primary key
6 | # listing_id :bigint not null
7 | # reservation_id :bigint
8 | # status :integer not null
9 | # start_date :date not null
10 | # end_date :date not null
11 | # created_at :datetime not null
12 | # updated_at :datetime not null
13 | #
14 |
15 | one:
16 | listing: one
17 | status: 1
18 | start_date: 2022-03-09
19 | end_date: 2022-03-09
20 | reservation: one
21 |
22 | two:
23 | listing: two
24 | status: 1
25 | start_date: 2022-03-09
26 | end_date: 2022-03-09
27 | reservation: two
28 |
--------------------------------------------------------------------------------
/test/models/reservation_test.rb:
--------------------------------------------------------------------------------
1 | # == Schema Information
2 | #
3 | # Table name: reservations
4 | #
5 | # id :bigint not null, primary key
6 | # listing_id :bigint not null
7 | # guest_id :bigint not null
8 | # session_id :string
9 | # status :integer default("pending")
10 | # created_at :datetime not null
11 | # updated_at :datetime not null
12 | # stripe_payment_intent_id :string
13 | # stripe_refund_id :string
14 | #
15 | require "test_helper"
16 |
17 | class ReservationTest < ActiveSupport::TestCase
18 | # test "the truth" do
19 | # assert true
20 | # end
21 | end
22 |
--------------------------------------------------------------------------------
/app/mailers/reservation_mailer.rb:
--------------------------------------------------------------------------------
1 | class ReservationMailer < ApplicationMailer
2 |
3 | # Subject can be set in your I18n file at config/locales/en.yml
4 | # with the following lookup:
5 | #
6 | # en.reservation_mailer.host_booked.subject
7 | #
8 | def host_booked
9 | @reservation = params[:reservation]
10 |
11 | mail to: @reservation.host.email, subject: "You have a new reservation!"
12 | end
13 |
14 | # Subject can be set in your I18n file at config/locales/en.yml
15 | # with the following lookup:
16 | #
17 | # en.reservation_mailer.guest_booked.subject
18 | #
19 | def guest_booked
20 | @reservation = params[:reservation]
21 |
22 | mail to: @reservation.guest.email, subject: "Your reservation is confirmed!"
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/application.css:
--------------------------------------------------------------------------------
1 | /*
2 | * This is a manifest file that'll be compiled into application.css, which will include all the files
3 | * listed below.
4 | *
5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's
6 | * vendor/assets/stylesheets directory can be referenced here using a relative path.
7 | *
8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9 | * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10 | * files in this directory. Styles in this file should be added after the last require_* statement.
11 | * It is generally better to create a new file per style scope.
12 | *
13 | *= require_tree .
14 | *= require_self
15 | */
16 |
--------------------------------------------------------------------------------
/app/views/reservations/new.html.erb:
--------------------------------------------------------------------------------
1 | <%= render partial: 'shared/errors' %>
2 |
3 |
4 | <% @calendar_events.each do |calendar_event| %>
5 |
6 | <%= calendar_event.start_date %>
7 | <%= calendar_event.end_date %>
8 | <%= calendar_event.status %>
9 | <%= calendar_event.try(:reservation).try(:status) %>
10 |
11 | <% end %>
12 |
13 |
14 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/notifications/guest_reservation_booked_notification.rb:
--------------------------------------------------------------------------------
1 | # To deliver this notification:
2 | #
3 | # GuestReservationBookedNotification.with(post: @post).deliver_later(current_user)
4 | # GuestReservationBookedNotification.with(post: @post).deliver(current_user)
5 |
6 | class GuestReservationBookedNotification < Noticed::Base
7 | # Add your delivery methods
8 | #
9 | deliver_by :database
10 | deliver_by :email, mailer: "ReservationMailer", method: :guest_booked
11 | # deliver_by :slack
12 | # deliver_by :custom, class: "MyDeliveryMethod"
13 |
14 | # Add required params
15 |
16 | param :reservation
17 |
18 | # Define helper methods to make rendering easier.
19 | #
20 | # def message
21 | # t(".message")
22 | # end
23 | #
24 | # def url
25 | # post_path(params[:post])
26 | # end
27 | end
28 |
--------------------------------------------------------------------------------
/app/controllers/messages_controller.rb:
--------------------------------------------------------------------------------
1 | class MessagesController < ApplicationController
2 | def index
3 | @reservation = current_user.all_reservations.find(params[:reservation_id])
4 | @messages = @reservation.messages
5 | @messages.where(to_user: current_user).mark_as_read!
6 | end
7 |
8 | def create
9 | @reservation = current_user.all_reservations.find(message_params[:reservation_id])
10 | @message = current_user.sent_messages.new(message_params)
11 | @message.to_user = @reservation.guest == current_user ? @reservation.host : @reservation.guest
12 | if !@message.save
13 | flash[:errors] = @message.errors.full_messages
14 | end
15 | redirect_to messages_path(reservation_id: @reservation.id)
16 | end
17 |
18 | private
19 |
20 | def message_params
21 | params.require(:message).permit(:content, :reservation_id)
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/app/notifications/host_reservation_booked_notification.rb:
--------------------------------------------------------------------------------
1 | # To deliver this notification:
2 | #
3 | # HostReservationBookedNotification.with(reservation: @reservation).deliver_later(current_user)
4 | # HostReservationBookedNotification.with(reservation: @reservation).deliver(current_user)
5 |
6 | class HostReservationBookedNotification < Noticed::Base
7 | # Add your delivery methods
8 | #
9 | deliver_by :database
10 | deliver_by :email, mailer: "ReservationMailer", method: :host_booked
11 | deliver_by :twilio
12 |
13 | # deliver_by :slack
14 | # deliver_by :custom, class: "MyDeliveryMethod"
15 |
16 | # Add required params
17 | #
18 | param :reservation
19 | param :message
20 |
21 | # Define helper methods to make rendering easier.
22 | #
23 | # def message
24 | # t(".message")
25 | # end
26 | #
27 | # def url
28 | # post_path(params[:post])
29 | # end
30 | end
31 |
--------------------------------------------------------------------------------
/app/controllers/host/rooms_controller.rb:
--------------------------------------------------------------------------------
1 | class Host::RoomsController < ApplicationController
2 | before_action :authenticate_user!
3 |
4 | def index
5 | @listing = current_user.listings.find(params[:listing_id])
6 | @rooms = @listing.rooms.all
7 | end
8 |
9 | def create
10 | @listing = current_user.listings.find(params[:listing_id])
11 | @room = @listing.rooms.new(room_params)
12 | if !@room.save
13 | flash[:errors] = @room.errors.full_messages
14 | end
15 |
16 | redirect_to host_listing_rooms_path(@listing)
17 | end
18 |
19 | def destroy
20 | @listing = current_user.listings.find(params[:listing_id])
21 | @room = @listing.rooms.find(params[:id])
22 | @room.destroy
23 | redirect_to host_listing_rooms_path(@listing)
24 | end
25 |
26 | private
27 |
28 | def room_params
29 | params.require(:room).permit(:room_type, beds_attributes: [:bed_size])
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/config/application.rb:
--------------------------------------------------------------------------------
1 | require_relative "boot"
2 |
3 | require "rails/all"
4 |
5 | # Require the gems listed in Gemfile, including any gems
6 | # you've limited to :test, :development, or :production.
7 | Bundler.require(*Rails.groups)
8 |
9 | module Clearbnb
10 | class Application < Rails::Application
11 | # Initialize configuration defaults for originally generated Rails version.
12 | config.load_defaults 6.1
13 |
14 | Rails.application.default_url_options = { host: 'localhost', port: 3000 }
15 | config.active_job.queue_adapter = :resque
16 |
17 | # Configuration for the application, engines, and railties goes here.
18 | #
19 | # These settings can be overridden in specific environments using the files
20 | # in config/environments, which are processed later.
21 | #
22 | # config.time_zone = "Central Time (US & Canada)"
23 | # config.eager_load_paths << Rails.root.join("extras")
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/config/credentials.yml.enc:
--------------------------------------------------------------------------------
1 | 6qEEo5tAcHeivumvh5DtcJpSkbk3m/VtaGYjEAmKUg87J5czlDnkEQM+Y8XKXz6RHo8KkCfgowbKYH5b4Zv5nrxb6HTZJgbt9jG9AdJ+/5GTQuG42MTN3bAON67f5NipnnYyeonIJVTZzSNTZxrAFdf3z7jzWJlpJw7tUy1Q0fVaDFfKjzhyQvo6mndIDS9j/G1w1KFLjYBIS4CcYEjJMbHJ9A4zRNsIayjvhdLgu4cTAQqXgMqRTSnGk+DM0r4+KC23V0FVmV1DMsPARKrqQ1rZsXGOXTZf1TZQ69X+QelpRoz+B3ULmEUY0hHj9uOgLIGbus1mN2XPj5MYXSk2QUzqSUS1wETOvzDRt5XsVZW6HYjdXYZqtfL44Y2hVn2mJdlG29fNfjBJj41SBCnabw3vJI7ZmyVj306MeAwSy0vygkGKkkVYoq1emhHuIoWvZdHYWqADuG81GprAyXPX5DG87MfQTRlbY6tTU5z/4tZ5ah6gPVEsNKAkBEgZN3w0IIlmoDof6C1oPRpjxVITN8d2n1m5EQkjD4EZI/KBz7e4GS8yz3Lyb5GpzhTyALeghGtGG6zEdoPac7X5MunWEeLxnFtkV5m350KJKuYccG82P8/3nD0BF4hFX+SXjf32i0AfQDBb9PzEbo/VYJ/05w+EJy//c4sDhOVDkznRM+HzULBdPLKkpe/VW1ntf1u8BbcJE3kVdJLntfdDrCHC7HgAHi4j+fw7yGYUghmhIsIvOuPbYcl9hJ1h2c25uSNvM3f61JIss4MhtKGBfzothl2nLCrR//t8blCFoariGERWWGs51C3iecLGIXHU0ctiUtGYIB472urU89GlZK5SDaA5G1H8Z0Xl6Z697KhTC5lwqjfEriiHJNWsLWxCg5dqQX3y05PbI4PrLeDV9HBX4TnViJMTMLWqyFEtJ382Sbo=--eXul164VPzM+KgSC--eruUFj1DznV1Jyw5U+Irtg==
--------------------------------------------------------------------------------
/app/views/devise/passwords/edit.html.erb:
--------------------------------------------------------------------------------
1 | Change your password
2 |
3 | <%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| %>
4 | <%= render "devise/shared/error_messages", resource: resource %>
5 | <%= f.hidden_field :reset_password_token %>
6 |
7 |
8 | <%= f.label :password, "New password" %>
9 | <% if @minimum_password_length %>
10 | (<%= @minimum_password_length %> characters minimum)
11 | <% end %>
12 | <%= f.password_field :password, autofocus: true, autocomplete: "new-password" %>
13 |
14 |
15 |
16 | <%= f.label :password_confirmation, "Confirm new password" %>
17 | <%= f.password_field :password_confirmation, autocomplete: "new-password" %>
18 |
19 |
20 |
21 | <%= f.submit "Change my password" %>
22 |
23 | <% end %>
24 |
25 | <%= render "devise/shared/links" %>
26 |
--------------------------------------------------------------------------------
/app/views/listings/show.html.erb:
--------------------------------------------------------------------------------
1 | <%= @listing.title %>
2 |
3 |
4 | Per night <%= number_to_currency(@listing.nightly_price / 100, precision: 0) %>
5 |
6 |
7 | Cleaning fee <%= number_to_currency(@listing.cleaning_fee / 100, precision: 0) %>
8 |
9 |
10 |
11 | <%= link_to "Reserve", new_reservation_path(listing_id: @listing.id) %>
12 |
13 | <%= @listing.city %>, <%= @listing.state %>
14 |
15 |
16 | <%= @listing.about %>
17 |
18 |
19 |
20 | <%= @listing.max_guests %>
21 |
22 |
23 |
24 | <% if @listing.photos.exists? %>
25 | <% @listing.photos.each do |photo| %>
26 | <%= image_tag photo.photo.variant(resize_to_limit: [100, 100]) %>
27 | <% end %>
28 | <% end %>
29 |
30 |
31 | <% @listing.rooms.each do |room| %>
32 | <%= room.room_type %> beds: <%= room.beds.count %>
33 | <% end %>
34 |
35 |
36 |
37 |
38 |
Host
39 | <%= @listing.host.email %>
40 |
41 |
--------------------------------------------------------------------------------
/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | # Files in the config/locales directory are used for internationalization
2 | # and are automatically loaded by Rails. If you want to use locales other
3 | # than English, add the necessary files in this directory.
4 | #
5 | # To use the locales, use `I18n.t`:
6 | #
7 | # I18n.t 'hello'
8 | #
9 | # In views, this is aliased to just `t`:
10 | #
11 | # <%= t('hello') %>
12 | #
13 | # To use a different locale, set it with `I18n.locale`:
14 | #
15 | # I18n.locale = :es
16 | #
17 | # This would use the information in config/locales/es.yml.
18 | #
19 | # The following keys must be escaped otherwise they will not be retrieved by
20 | # the default I18n backend:
21 | #
22 | # true, false, on, off, yes, no
23 | #
24 | # Instead, surround them with single quotes.
25 | #
26 | # en:
27 | # 'true': 'foo'
28 | #
29 | # To learn more, please read the Rails Internationalization guide
30 | # available at https://guides.rubyonrails.org/i18n.html.
31 |
32 | en:
33 | hello: "Hello world"
34 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files.
2 | #
3 | # If you find yourself ignoring temporary files generated by your text editor
4 | # or operating system, you probably want to add a global ignore instead:
5 | # git config --global core.excludesfile '~/.gitignore_global'
6 |
7 | # Ignore bundler config.
8 | /.bundle
9 |
10 | # Ignore all logfiles and tempfiles.
11 | /log/*
12 | /tmp/*
13 | !/log/.keep
14 | !/tmp/.keep
15 |
16 | # Ignore pidfiles, but keep the directory.
17 | /tmp/pids/*
18 | !/tmp/pids/
19 | !/tmp/pids/.keep
20 |
21 | # Ignore uploaded files in development.
22 | /storage/*
23 | !/storage/.keep
24 |
25 | /public/assets
26 | .byebug_history
27 |
28 | # Ignore master key for decrypting credentials and more.
29 | /config/master.key
30 |
31 | /public/packs
32 | /public/packs-test
33 | /node_modules
34 | /yarn-error.log
35 | yarn-debug.log*
36 | .yarn-integrity
37 | dump.rdb
38 |
39 | /app/assets/builds/*
40 | !/app/assets/builds/.keep
41 |
--------------------------------------------------------------------------------
/app/notifications/new_message.rb:
--------------------------------------------------------------------------------
1 | # To deliver this notification:
2 | #
3 | # NewMessage.with(message: @message).deliver_later(current_user)
4 | # NewMessage.with(message: @message).deliver(current_user)
5 |
6 | class NewMessage < Noticed::Base
7 | # Add your delivery methods
8 | #
9 | deliver_by :database
10 | deliver_by :email, mailer: "MessageMailer", method: :new_message
11 | deliver_by :twilio, format: :format_for_twilio
12 |
13 | # deliver_by :slack
14 | # deliver_by :custom, class: "MyDeliveryMethod"
15 |
16 | # Add required params
17 | #
18 | param :message
19 |
20 | def format_for_twilio
21 | {
22 | Body: params[:message].content,
23 | From: Rails.application.credentials.twilio[:phone_number],
24 | To: recipient.phone_number
25 | }
26 | end
27 |
28 | # Define helper methods to make rendering easier.
29 | #
30 | # def message
31 | # t(".message")
32 | # end
33 | #
34 | # def url
35 | # post_path(params[:post])
36 | # end
37 | end
38 |
--------------------------------------------------------------------------------
/app/models/reservation.rb:
--------------------------------------------------------------------------------
1 | # == Schema Information
2 | #
3 | # Table name: reservations
4 | #
5 | # id :bigint not null, primary key
6 | # listing_id :bigint not null
7 | # guest_id :bigint not null
8 | # session_id :string
9 | # status :integer default("pending")
10 | # created_at :datetime not null
11 | # updated_at :datetime not null
12 | # stripe_payment_intent_id :string
13 | # stripe_refund_id :string
14 | #
15 | class Reservation < ApplicationRecord
16 | belongs_to :listing
17 | belongs_to :guest, class_name: 'User'
18 | has_one :host, through: :listing
19 | enum status: {pending: 0, booked: 1, canceling: 3, cancelled: 2, expired: 4}
20 | has_one :calendar_event, dependent: :destroy, required: true
21 | has_many :messages
22 |
23 | delegate :start_date, to: :calendar_event
24 | delegate :end_date, to: :calendar_event
25 | delegate :nights, to: :calendar_event
26 | end
27 |
--------------------------------------------------------------------------------
/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require "fileutils"
3 |
4 | # path to your application root.
5 | APP_ROOT = File.expand_path("..", __dir__)
6 |
7 | def system!(*args)
8 | system(*args) || abort("\n== Command #{args} failed ==")
9 | end
10 |
11 | FileUtils.chdir APP_ROOT do
12 | # This script is a way to set up or update your development environment automatically.
13 | # This script is idempotent, so that you can run it at any time and get an expectable outcome.
14 | # Add necessary setup steps to this file.
15 |
16 | puts "== Installing dependencies =="
17 | system! "gem install bundler --conservative"
18 | system("bundle check") || system!("bundle install")
19 |
20 | # puts "\n== Copying sample files =="
21 | # unless File.exist?("config/database.yml")
22 | # FileUtils.cp "config/database.yml.sample", "config/database.yml"
23 | # end
24 |
25 | puts "\n== Preparing database =="
26 | system! "bin/rails db:prepare"
27 |
28 | puts "\n== Removing old logs and tempfiles =="
29 | system! "bin/rails log:clear tmp:clear"
30 |
31 | puts "\n== Restarting application server =="
32 | system! "bin/rails restart"
33 | end
34 |
--------------------------------------------------------------------------------
/app/javascript/controllers/listing_map_controller.js:
--------------------------------------------------------------------------------
1 | import { Controller } from "@hotwired/stimulus"
2 |
3 | // Connects to data-controller="listing_map"
4 | export default class extends Controller {
5 | static targets = ["map", "listings"]
6 | connect() {
7 | console.log("ListingMapController connected")
8 |
9 | if(window.google) {
10 | this.initGoogle();
11 | }
12 | }
13 |
14 | initGoogle() {
15 | console.log('init google')
16 | const myLatLng = { lat: -25.363, lng: 131.044 };
17 | const map = new google.maps.Map(this.mapTarget, {
18 | zoom: 4,
19 | center: myLatLng,
20 | });
21 | this.addMarkers(map);
22 | }
23 |
24 | addMarkers(map) {
25 | console.log('adding markers')
26 | Array.from(this.listingsTarget.children).forEach((listing) => {
27 | if(listing.dataset.lat) {
28 | new google.maps.Marker({
29 | position: {
30 | lat: parseFloat(listing.dataset.lat),
31 | lng: parseFloat(listing.dataset.lng),
32 | },
33 | map,
34 | title: listing.dataset.title,
35 | });
36 | }
37 | })
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/config/storage.yml:
--------------------------------------------------------------------------------
1 | test:
2 | service: Disk
3 | root: <%= Rails.root.join("tmp/storage") %>
4 |
5 | local:
6 | service: Disk
7 | root: <%= Rails.root.join("storage") %>
8 |
9 | # Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
10 | # amazon:
11 | # service: S3
12 | # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
13 | # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
14 | # region: us-east-1
15 | # bucket: your_own_bucket
16 |
17 | # Remember not to checkin your GCS keyfile to a repository
18 | # google:
19 | # service: GCS
20 | # project: your_project
21 | # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %>
22 | # bucket: your_own_bucket
23 |
24 | # Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
25 | # microsoft:
26 | # service: AzureStorage
27 | # storage_account_name: your_account_name
28 | # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>
29 | # container: your_container_name
30 |
31 | # mirror:
32 | # service: Mirror
33 | # primary: local
34 | # mirrors: [ amazon, google, microsoft ]
35 |
--------------------------------------------------------------------------------
/app/controllers/host/merchant_settings_controller.rb:
--------------------------------------------------------------------------------
1 | class Host::MerchantSettingsController < ApplicationController
2 | before_action :authenticate_user!
3 |
4 | def index
5 | end
6 |
7 | def connect
8 | if !current_user.stripe_account_id.present?
9 | account = Stripe::Account.create(
10 | type: 'express',
11 | country: 'US',
12 | email: current_user.email,
13 | capabilities: {
14 | card_payments: {requested: true},
15 | transfers: {requested: true},
16 | tax_reporting_us_1099_k: {requested: true},
17 | },
18 | business_type: 'individual',
19 | individual: {
20 | email: current_user.email,
21 | }
22 | )
23 | current_user.update(
24 | is_host: true,
25 | stripe_account_id: account.id,
26 | )
27 | end
28 |
29 | link = Stripe::AccountLink.create(
30 | account: current_user.stripe_account_id,
31 | refresh_url: connect_host_merchant_settings_url,
32 | return_url: connected_host_merchant_settings_url,
33 | type: 'account_onboarding',
34 | )
35 | redirect_to link.url, status: :see_other, allow_other_hosts: true
36 | end
37 |
38 | def connected
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/app/views/host/rooms/index.html.erb:
--------------------------------------------------------------------------------
1 | Rooms
2 |
3 | <%= link_to "Back to #{@listing.title}", host_listing_path(@listing) %>
4 |
5 |
6 | <% @rooms.each do |room| %>
7 |
8 |
9 |
10 |
11 |
<%= room.room_type %>
12 |
13 | <%= room.beds.count %>
14 |
15 |
16 | <% room.beds.each do |bed| %>
17 |
<%= bed.bed_size %>
18 | <% end %>
19 | <%= button_to "Delete", host_listing_room_path(@listing, room), method: :delete %>
20 |
21 |
22 |
23 | <% end %>
24 |
25 |
26 | <%= render partial: 'form', locals: { listing: @listing } %>
27 |
--------------------------------------------------------------------------------
/config/routes.rb:
--------------------------------------------------------------------------------
1 | require 'resque/server'
2 | Rails.application.routes.draw do
3 | root to: 'static_pages#home'
4 |
5 | resources :listings, only: [:index, :show]
6 | resources :messages, only: [:index, :create]
7 | resources :reservations do
8 | member do
9 | post '/cancel' => 'reservations#cancel'
10 | get '/expire' => 'reservations#expire'
11 | end
12 | end
13 | post '/webhooks/:source' => 'webhooks#create'
14 |
15 | namespace :host do
16 | resources :merchant_settings do
17 | collection do
18 | get :connect, to: 'merchant_settings#connect'
19 | get :connected, to: 'merchant_settings#connected'
20 | end
21 | end
22 | resources :listings do
23 | resources :photos, only: [:index, :create, :destroy]
24 | resources :rooms, only: [:index, :create, :destroy]
25 | end
26 | resources :reservations, only: [:show]
27 | end
28 |
29 | devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks' }
30 |
31 | resque_web_constraint = lambda do |request|
32 | current_user = request.env['warden'].user
33 | current_user.id == 1
34 | end
35 | constraints resque_web_constraint do
36 | mount Resque::Server, at: '/jobs'
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/app/views/devise/shared/_links.html.erb:
--------------------------------------------------------------------------------
1 | <%- if controller_name != 'sessions' %>
2 | <%= link_to "Log in", new_session_path(resource_name) %>
3 | <% end %>
4 |
5 | <%- if devise_mapping.registerable? && controller_name != 'registrations' %>
6 | <%= link_to "Sign up", new_registration_path(resource_name) %>
7 | <% end %>
8 |
9 | <%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %>
10 | <%= link_to "Forgot your password?", new_password_path(resource_name) %>
11 | <% end %>
12 |
13 | <%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
14 | <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %>
15 | <% end %>
16 |
17 | <%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
18 | <%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %>
19 | <% end %>
20 |
21 | <%- if devise_mapping.omniauthable? %>
22 | <%- resource_class.omniauth_providers.each do |provider| %>
23 | <%= link_to "Sign in with #{OmniAuth::Utils.camelize(provider)}", omniauth_authorize_path(resource_name, provider), method: :post %>
24 | <% end %>
25 | <% end %>
26 |
--------------------------------------------------------------------------------
/db/seeds.rb:
--------------------------------------------------------------------------------
1 | me = User.find_or_create_by!(
2 | email: 'wave@cjav.dev',
3 | ) do |u|
4 | u.password = 'password'
5 | end
6 |
7 | 10.times do
8 | listing = Listing.create(
9 | host: me,
10 | title: Faker::Lorem.words.join(" "),
11 | about: Faker::Lorem.paragraphs.join("\n"),
12 | max_guests: (1...15).to_a.sample,
13 | address_line1: Faker::Address.street_address,
14 | city: Faker::Address.city,
15 | state: Faker::Address.state,
16 | country: 'US',
17 | status: [:draft, :published].sample,
18 | nightly_price: 12000,
19 | cleaning_fee: 5000,
20 | )
21 | puts "New listing: #{listing.id}"
22 | end
23 |
24 |
25 | 10.times do
26 | host = User.create!(
27 | email: Faker::Internet.email,
28 | password: Faker::Internet.password
29 | )
30 |
31 | 10.times do
32 | listing = Listing.create(
33 | host: host,
34 | title: Faker::Lorem.words.join(" "),
35 | about: Faker::Lorem.paragraphs.join("\n"),
36 | max_guests: (1...15).to_a.sample,
37 | address_line1: Faker::Address.street_address,
38 | city: Faker::Address.city,
39 | state: Faker::Address.state,
40 | country: 'US',
41 | status: [:draft, :published].sample,
42 | nightly_price: 12000,
43 | cleaning_fee: 5000,
44 | )
45 | end
46 | end
47 |
48 |
49 |
--------------------------------------------------------------------------------
/config/initializers/content_security_policy.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Define an application-wide content security policy
4 | # For further information see the following documentation
5 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
6 |
7 | # Rails.application.config.content_security_policy do |policy|
8 | # policy.default_src :self, :https
9 | # policy.font_src :self, :https, :data
10 | # policy.img_src :self, :https, :data
11 | # policy.object_src :none
12 | # policy.script_src :self, :https
13 | # policy.style_src :self, :https
14 | # # Specify URI for violation reports
15 | # # policy.report_uri "/csp-violation-report-endpoint"
16 | # end
17 |
18 | # If you are using UJS then enable automatic nonce generation
19 | # Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
20 |
21 | # Set the nonce only to specific directives
22 | # Rails.application.config.content_security_policy_nonce_directives = %w(script-src)
23 |
24 | # Report CSP violations to a specified URI
25 | # For further information see the following documentation:
26 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
27 | # Rails.application.config.content_security_policy_report_only = true
28 |
--------------------------------------------------------------------------------
/app/views/host/rooms/_form.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Bed size
5 |
6 | <% Bed.bed_sizes.each do |bed_size, _| %>
7 | <%= bed_size %>
8 | <% end %>
9 |
10 |
11 |
12 |
13 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/app/views/host/listings/edit.html.erb:
--------------------------------------------------------------------------------
1 | Edit <%= @listing.title %>
2 |
3 | <%= render partial: "shared/errors" %>
4 |
5 |
33 |
--------------------------------------------------------------------------------
/app/models/calendar_event.rb:
--------------------------------------------------------------------------------
1 | # == Schema Information
2 | #
3 | # Table name: calendar_events
4 | #
5 | # id :bigint not null, primary key
6 | # listing_id :bigint not null
7 | # reservation_id :bigint
8 | # status :integer not null
9 | # start_date :date not null
10 | # end_date :date not null
11 | # created_at :datetime not null
12 | # updated_at :datetime not null
13 | #
14 | class CalendarEvent < ApplicationRecord
15 | belongs_to :listing
16 | belongs_to :reservation, optional: true
17 | validates :status, presence: true
18 | enum status: { reserved: 0, blocked: 1 } # blocked means manually held by the host
19 |
20 | validates :start_date, presence: true
21 | validates :end_date, presence: true
22 | # Start date cannot fall after end date
23 | validates :start_date, comparison: { less_than: :end_date }
24 | # Dates must be in the future
25 | validates :start_date, inclusion: {
26 | in: (Date.today..Date.today + 365 + 365),
27 | message: "must be in the future"
28 | }
29 | # Dates don't overlap with other calendar events for this listing
30 | validates :start_date, :end_date, overlap: {
31 | exclude_edges: ['start_date', 'end_date'],
32 | scope: :listing_id,
33 | query_options: {
34 | joins: 'LEFT OUTER JOIN reservations r on r.id = calendar_events.reservation_id',
35 | where: 'r.status in (0, 1) or calendar_events.status = 1'
36 | },
37 | message_content: 'is already booked for this date range'
38 | }
39 |
40 | def nights
41 | (start_date...end_date).count
42 | end
43 | end
44 |
--------------------------------------------------------------------------------
/app/views/devise/registrations/edit.html.erb:
--------------------------------------------------------------------------------
1 | Edit <%= resource_name.to_s.humanize %>
2 |
3 | <%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
4 | <%= render "devise/shared/error_messages", resource: resource %>
5 |
6 |
7 | <%= f.label :email %>
8 | <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
9 |
10 |
11 | <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
12 | Currently waiting confirmation for: <%= resource.unconfirmed_email %>
13 | <% end %>
14 |
15 |
16 | <%= f.label :password %> (leave blank if you don't want to change it)
17 | <%= f.password_field :password, autocomplete: "new-password" %>
18 | <% if @minimum_password_length %>
19 |
20 | <%= @minimum_password_length %> characters minimum
21 | <% end %>
22 |
23 |
24 |
25 | <%= f.label :password_confirmation %>
26 | <%= f.password_field :password_confirmation, autocomplete: "new-password" %>
27 |
28 |
29 |
30 | <%= f.label :current_password %> (we need your current password to confirm your changes)
31 | <%= f.password_field :current_password, autocomplete: "current-password" %>
32 |
33 |
34 |
35 | <%= f.submit "Update" %>
36 |
37 | <% end %>
38 |
39 | Cancel my account
40 |
41 | Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %>
42 |
43 | <%= link_to "Back", :back %>
44 |
--------------------------------------------------------------------------------
/app/views/listings/index.html.erb:
--------------------------------------------------------------------------------
1 | Listings
2 |
3 |
4 | <%== pagy_nav(@pagy) %>
5 |
6 |
7 |
25 |
26 |
27 | <%== pagy_nav(@pagy) %>
28 |
29 |
--------------------------------------------------------------------------------
/app/controllers/host/listings_controller.rb:
--------------------------------------------------------------------------------
1 | class Host::ListingsController < ApplicationController
2 | before_action :authenticate_user!
3 |
4 | def new
5 | @listing = Listing.new
6 | end
7 |
8 | def create
9 | @listing = current_user.listings.new(listing_create_params)
10 | if @listing.save
11 | redirect_to host_listing_path(@listing)
12 | else
13 | flash.now[:errors] = @listing.errors.full_messages
14 | render :new
15 | end
16 | end
17 |
18 | def edit
19 | @listing = current_user.listings.find(params[:id])
20 | end
21 |
22 | def update
23 | @listing = current_user.listings.find(params[:id])
24 | if @listing.update(listing_update_params)
25 | redirect_to host_listing_path(@listing)
26 | else
27 | flash.now[:errors] = @listing.errors.full_messages
28 | render :edit
29 | end
30 | end
31 |
32 | def index
33 | @listings = current_user.listings.all
34 | end
35 |
36 | def show
37 | @listing = current_user.listings.find(params[:id])
38 | end
39 |
40 | def destroy
41 | @listing = current_user.listings.find(params[:id])
42 | @listing.update(status: :archived)
43 | redirect_to host_listings_path
44 | end
45 |
46 | private
47 |
48 | def listing_create_params
49 | params.require(:listing).permit(
50 | :title,
51 | :about,
52 | :max_guests,
53 | :address_line1,
54 | :address_line2,
55 | :city,
56 | :state,
57 | :postal_code,
58 | :country,
59 | :lat,
60 | :lng,
61 | :nightly_price,
62 | :cleaning_fee
63 | )
64 | end
65 |
66 | def listing_update_params
67 | params.require(:listing).permit(
68 | :title,
69 | :about,
70 | :max_guests,
71 | :status,
72 | :nightly_price,
73 | :cleaning_fee
74 | )
75 | end
76 | end
77 |
--------------------------------------------------------------------------------
/public/500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | We're sorry, but something went wrong (500)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
We're sorry, but something went wrong.
62 |
63 |
If you are the application owner check the logs for more information.
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/config/puma.rb:
--------------------------------------------------------------------------------
1 | # Puma can serve each request in a thread from an internal thread pool.
2 | # The `threads` method setting takes two numbers: a minimum and maximum.
3 | # Any libraries that use thread pools should be configured to match
4 | # the maximum value specified for Puma. Default is set to 5 threads for minimum
5 | # and maximum; this matches the default thread size of Active Record.
6 | #
7 | max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
8 | min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count }
9 | threads min_threads_count, max_threads_count
10 |
11 | # Specifies the `worker_timeout` threshold that Puma will use to wait before
12 | # terminating a worker in development environments.
13 | #
14 | worker_timeout 3600 if ENV.fetch("RAILS_ENV", "development") == "development"
15 |
16 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000.
17 | #
18 | port ENV.fetch("PORT") { 3000 }
19 |
20 | # Specifies the `environment` that Puma will run in.
21 | #
22 | environment ENV.fetch("RAILS_ENV") { "development" }
23 |
24 | # Specifies the `pidfile` that Puma will use.
25 | pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" }
26 |
27 | # Specifies the number of `workers` to boot in clustered mode.
28 | # Workers are forked web server processes. If using threads and workers together
29 | # the concurrency of the application would be max `threads` * `workers`.
30 | # Workers do not work on JRuby or Windows (both of which do not support
31 | # processes).
32 | #
33 | # workers ENV.fetch("WEB_CONCURRENCY") { 2 }
34 |
35 | # Use the `preload_app!` method when specifying a `workers` number.
36 | # This directive tells Puma to first boot the application and load code
37 | # before forking the application. This takes advantage of Copy On Write
38 | # process behavior so workers use less memory.
39 | #
40 | # preload_app!
41 |
42 | # Allow puma to be restarted by `rails restart` command.
43 | plugin :tmp_restart
44 |
--------------------------------------------------------------------------------
/db/migrate/20210926175127_add_devise_to_users.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class AddDeviseToUsers < ActiveRecord::Migration[6.1]
4 | def self.up
5 | change_table :users do |t|
6 | ## Database authenticatable
7 | t.string :email, null: false, default: ""
8 | t.string :encrypted_password, null: false, default: ""
9 |
10 | ## Recoverable
11 | t.string :reset_password_token
12 | t.datetime :reset_password_sent_at
13 |
14 | ## Rememberable
15 | t.datetime :remember_created_at
16 |
17 | ## Trackable
18 | t.integer :sign_in_count, default: 0, null: false
19 | t.datetime :current_sign_in_at
20 | t.datetime :last_sign_in_at
21 | t.string :current_sign_in_ip
22 | t.string :last_sign_in_ip
23 |
24 | ## Confirmable
25 | t.string :confirmation_token
26 | t.datetime :confirmed_at
27 | t.datetime :confirmation_sent_at
28 | t.string :unconfirmed_email # Only if using reconfirmable
29 |
30 | ## Lockable
31 | t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
32 | t.string :unlock_token # Only if unlock strategy is :email or :both
33 | t.datetime :locked_at
34 |
35 | # Uncomment below if timestamps were not included in your original model.
36 | # t.timestamps null: false
37 | end
38 |
39 | add_index :users, :email, unique: true
40 | add_index :users, :reset_password_token, unique: true
41 | add_index :users, :confirmation_token, unique: true
42 | add_index :users, :unlock_token, unique: true
43 | end
44 |
45 | def self.down
46 | # By default, we don't want to make any assumption about how to roll back a migration when your
47 | # model already existed. Please edit below which fields you would like to remove in this migration.
48 | raise ActiveRecord::IrreversibleMigration
49 | end
50 | end
51 |
--------------------------------------------------------------------------------
/app/models/listing.rb:
--------------------------------------------------------------------------------
1 | # == Schema Information
2 | #
3 | # Table name: listings
4 | #
5 | # id :bigint not null, primary key
6 | # host_id :bigint not null
7 | # title :string not null
8 | # about :text
9 | # max_guests :integer default(1)
10 | # address_line1 :string
11 | # address_line2 :string
12 | # city :string
13 | # state :string
14 | # postal_code :string
15 | # country :string
16 | # lat :decimal(10, 6)
17 | # lng :decimal(10, 6)
18 | # status :integer default("draft")
19 | # created_at :datetime not null
20 | # updated_at :datetime not null
21 | # nightly_price :integer
22 | # cleaning_fee :integer
23 | # stripe_product_id :string
24 | #
25 | class Listing < ApplicationRecord
26 | validates :nightly_price, numericality: { greater_than: 0 }
27 | validates :cleaning_fee, numericality: { greater_than: 0 }
28 |
29 | validates :title, presence: true
30 | validates :max_guests, numericality: { greater_than: 0, less_than_or_equal_to: 100 }
31 | belongs_to :host, class_name: 'User'
32 | enum status: {draft: 0, published: 1, archived: 2}
33 | has_many :rooms
34 | has_many :photos
35 | has_many :calendar_events
36 | has_many :reservations
37 | scope :published, -> { where(status: :published) }
38 |
39 | after_commit :maybe_create_stripe_product, on: [:create, :update]
40 |
41 | def maybe_create_stripe_product
42 | return if !stripe_product_id.blank?
43 |
44 | product = Stripe::Product.create(
45 | name: title,
46 | url: Rails.application.routes.url_helpers.url_for(self),
47 | metadata: {
48 | clearbnb_id: id,
49 | }
50 | )
51 | update(stripe_product_id: product.id)
52 | end
53 |
54 | def address
55 | "#{address_line1} #{address_line2} #{city} #{state}"
56 | end
57 | end
58 |
--------------------------------------------------------------------------------
/public/422.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The change you wanted was rejected (422)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
The change you wanted was rejected.
62 |
Maybe you tried to change something you didn't have access to.
63 |
64 |
If you are the application owner check the logs for more information.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The page you were looking for doesn't exist (404)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
The page you were looking for doesn't exist.
62 |
You may have mistyped the address or the page may have moved.
63 |
64 |
If you are the application owner check the logs for more information.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/app/controllers/reservations_controller.rb:
--------------------------------------------------------------------------------
1 | class ReservationsController < ApplicationController
2 | before_action :authenticate_user!
3 |
4 | def index
5 | @reservations = current_user.reservations
6 | @host_reservations = current_user.host_reservations
7 | end
8 |
9 | def expire
10 | Stripe::Checkout::Session.expire(params[:session_id])
11 | redirect_to listings_path
12 | end
13 |
14 | def cancel
15 | @reservation = current_user.reservations.find(params[:id])
16 | # make api call to refund payment
17 | refund = Stripe::Refund.create({
18 | payment_intent: @reservation.stripe_payment_intent_id,
19 | })
20 | @reservation.update(status: :canceling, stripe_refund_id: refund.id)
21 | redirect_to reservation_path(@reservation)
22 | end
23 |
24 | def new
25 | @listing = Listing.find(params[:listing_id])
26 | @calendar_events = @listing.calendar_events
27 | end
28 |
29 | def create
30 | # check to see if the user's identity is verified
31 | if !current_user.identity_verified
32 | # Rdirect through the identity verification flow
33 | verification_session = Stripe::Identity::VerificationSession.create({
34 | type: 'document',
35 | metadata: {
36 | user_id: current_user.id,
37 | },
38 | return_url: new_reservation_url(listing_id: reservation_params[:listing_id]),
39 | })
40 | redirect_to verification_session.url, allow_other_host: true, status: :see_other
41 | return
42 | end
43 |
44 | @booking = BookListing.new(current_user, reservation_params)
45 |
46 | if @booking.save
47 | redirect_to @booking.checkout_url, allow_other_hosts: true, status: :see_other
48 | else
49 | flash.now[:errors] = @booking.errors
50 | @listing = @booking.listing
51 | @calendar_events = @listing.calendar_events
52 | render :new
53 | end
54 | end
55 |
56 | def show
57 | @reservation = current_user.reservations.find(params[:id])
58 | end
59 |
60 | private
61 |
62 | def reservation_params
63 | params.require(:reservation).permit(:listing_id, :start_date, :end_date)
64 | end
65 | end
66 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function(api) {
2 | var validEnv = ['development', 'test', 'production']
3 | var currentEnv = api.env()
4 | var isDevelopmentEnv = api.env('development')
5 | var isProductionEnv = api.env('production')
6 | var isTestEnv = api.env('test')
7 |
8 | if (!validEnv.includes(currentEnv)) {
9 | throw new Error(
10 | 'Please specify a valid `NODE_ENV` or ' +
11 | '`BABEL_ENV` environment variables. Valid values are "development", ' +
12 | '"test", and "production". Instead, received: ' +
13 | JSON.stringify(currentEnv) +
14 | '.'
15 | )
16 | }
17 |
18 | return {
19 | presets: [
20 | isTestEnv && [
21 | '@babel/preset-env',
22 | {
23 | targets: {
24 | node: 'current'
25 | }
26 | }
27 | ],
28 | (isProductionEnv || isDevelopmentEnv) && [
29 | '@babel/preset-env',
30 | {
31 | forceAllTransforms: true,
32 | useBuiltIns: 'entry',
33 | corejs: 3,
34 | modules: false,
35 | exclude: ['transform-typeof-symbol']
36 | }
37 | ]
38 | ].filter(Boolean),
39 | plugins: [
40 | 'babel-plugin-macros',
41 | '@babel/plugin-syntax-dynamic-import',
42 | isTestEnv && 'babel-plugin-dynamic-import-node',
43 | '@babel/plugin-transform-destructuring',
44 | [
45 | '@babel/plugin-proposal-class-properties',
46 | {
47 | loose: true
48 | }
49 | ],
50 | [
51 | '@babel/plugin-proposal-object-rest-spread',
52 | {
53 | useBuiltIns: true
54 | }
55 | ],
56 | [
57 | '@babel/plugin-proposal-private-methods',
58 | {
59 | loose: true
60 | }
61 | ],
62 | [
63 | '@babel/plugin-proposal-private-property-in-object',
64 | {
65 | loose: true
66 | }
67 | ],
68 | [
69 | '@babel/plugin-transform-runtime',
70 | {
71 | helpers: false
72 | }
73 | ],
74 | [
75 | '@babel/plugin-transform-regenerator',
76 | {
77 | async: false
78 | }
79 | ]
80 | ].filter(Boolean)
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 | git_source(:github) { |repo| "https://github.com/#{repo}.git" }
3 |
4 | ruby '3.1.2'
5 |
6 | gem 'rails', github: 'rails/rails', branch: 'main'
7 |
8 | gem 'sprockets-rails'
9 |
10 | # Use postgresql as the database for Active Record
11 | gem 'pg', '~> 1.1'
12 |
13 | # Use Puma as the app server
14 | gem 'puma', '~> 5.0'
15 |
16 | gem "jsbundling-rails"
17 |
18 | gem "turbo-rails"
19 |
20 | gem "stimulus-rails"
21 |
22 | # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
23 | gem 'jbuilder', '~> 2.7'
24 |
25 | # Use SCSS for stylesheets
26 | gem 'sass-rails', '>= 6'
27 |
28 |
29 |
30 | # Use Redis adapter to run Action Cable in production
31 | # gem 'redis', '~> 4.0'
32 | # Use Active Model has_secure_password
33 | # gem 'bcrypt', '~> 3.1.7'
34 |
35 | # Use Active Storage variant
36 | # gem 'image_processing', '~> 1.2'
37 |
38 | # Reduces boot times through caching; required in config/boot.rb
39 | gem 'bootsnap', require: false
40 |
41 | # Authentication
42 | gem 'devise'
43 | gem 'omniauth-google-oauth2'
44 | gem 'omniauth-rails_csrf_protection'
45 |
46 | group :development, :test do
47 | # Call 'byebug' anywhere in the code to stop execution and get a debugger console
48 | gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
49 | gem "pry-rails", "~> 0.3.9"
50 | gem 'faker'
51 | end
52 |
53 | group :development do
54 | # Access an interactive console on exception pages or by calling 'console' anywhere in the code.
55 | gem 'web-console', '>= 4.1.0'
56 | # Display performance information such as SQL time and flame graphs for each request in your browser.
57 | # Can be configured to work on production as well see: https://github.com/MiniProfiler/rack-mini-profiler/blob/master/README.md
58 | gem 'rack-mini-profiler', '~> 2.0'
59 | gem 'listen', '~> 3.3'
60 | # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
61 | gem 'spring'
62 | gem 'letter_opener'
63 | gem 'annotate'
64 | end
65 |
66 | # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
67 | gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
68 |
69 | gem "tailwindcss-rails", "~> 0.4.3"
70 | gem 'image_processing', '~> 1.2'
71 | gem 'stripe'
72 | gem 'resque'
73 | gem "validates_overlap", "~> 1.0"
74 | gem "pagy", "~> 5.10"
75 | gem "noticed", "~> 1.5"
76 |
--------------------------------------------------------------------------------
/db/migrate/20211126230823_create_active_storage_tables.active_storage.rb:
--------------------------------------------------------------------------------
1 | # This migration comes from active_storage (originally 20170806125915)
2 | class CreateActiveStorageTables < ActiveRecord::Migration[5.2]
3 | def change
4 | # Use Active Record's configured type for primary and foreign keys
5 | primary_key_type, foreign_key_type = primary_and_foreign_key_types
6 |
7 | create_table :active_storage_blobs, id: primary_key_type do |t|
8 | t.string :key, null: false
9 | t.string :filename, null: false
10 | t.string :content_type
11 | t.text :metadata
12 | t.string :service_name, null: false
13 | t.bigint :byte_size, null: false
14 | t.string :checksum, null: false
15 |
16 | if connection.supports_datetime_with_precision?
17 | t.datetime :created_at, precision: 6, null: false
18 | else
19 | t.datetime :created_at, null: false
20 | end
21 |
22 | t.index [ :key ], unique: true
23 | end
24 |
25 | create_table :active_storage_attachments, id: primary_key_type do |t|
26 | t.string :name, null: false
27 | t.references :record, null: false, polymorphic: true, index: false, type: foreign_key_type
28 | t.references :blob, null: false, type: foreign_key_type
29 |
30 | if connection.supports_datetime_with_precision?
31 | t.datetime :created_at, precision: 6, null: false
32 | else
33 | t.datetime :created_at, null: false
34 | end
35 |
36 | t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true
37 | t.foreign_key :active_storage_blobs, column: :blob_id
38 | end
39 |
40 | create_table :active_storage_variant_records, id: primary_key_type do |t|
41 | t.belongs_to :blob, null: false, index: false, type: foreign_key_type
42 | t.string :variation_digest, null: false
43 |
44 | t.index %i[ blob_id variation_digest ], name: "index_active_storage_variant_records_uniqueness", unique: true
45 | t.foreign_key :active_storage_blobs, column: :blob_id
46 | end
47 | end
48 |
49 | private
50 | def primary_and_foreign_key_types
51 | config = Rails.configuration.generators
52 | setting = config.options[config.orm][:primary_key_type]
53 | primary_key_type = setting || :primary_key
54 | foreign_key_type = setting || :bigint
55 | [primary_key_type, foreign_key_type]
56 | end
57 | end
58 |
--------------------------------------------------------------------------------
/config/environments/test.rb:
--------------------------------------------------------------------------------
1 | require "active_support/core_ext/integer/time"
2 |
3 | # The test environment is used exclusively to run your application's
4 | # test suite. You never need to work with it otherwise. Remember that
5 | # your test database is "scratch space" for the test suite and is wiped
6 | # and recreated between test runs. Don't rely on the data there!
7 |
8 | Rails.application.configure do
9 | # Settings specified here will take precedence over those in config/application.rb.
10 |
11 | # Turn false under Spring and add config.action_view.cache_template_loading = true
12 | config.cache_classes = true
13 |
14 | # Do not eager load code on boot. This avoids loading your whole application
15 | # just for the purpose of running a single test. If you are using a tool that
16 | # preloads Rails for running tests, you may have to set it to true.
17 | config.eager_load = false
18 |
19 | # Configure public file server for tests with Cache-Control for performance.
20 | config.public_file_server.enabled = true
21 | config.public_file_server.headers = {
22 | "Cache-Control" => "public, max-age=#{1.hour.to_i}"
23 | }
24 |
25 | # Show full error reports and disable caching.
26 | config.consider_all_requests_local = true
27 | config.action_controller.perform_caching = false
28 | config.cache_store = :null_store
29 |
30 | # Raise exceptions instead of rendering exception templates.
31 | config.action_dispatch.show_exceptions = false
32 |
33 | # Disable request forgery protection in test environment.
34 | config.action_controller.allow_forgery_protection = false
35 |
36 | # Store uploaded files on the local file system in a temporary directory.
37 | config.active_storage.service = :test
38 |
39 | config.action_mailer.perform_caching = false
40 |
41 | # Tell Action Mailer not to deliver emails to the real world.
42 | # The :test delivery method accumulates sent emails in the
43 | # ActionMailer::Base.deliveries array.
44 | config.action_mailer.delivery_method = :test
45 |
46 | # Print deprecation notices to the stderr.
47 | config.active_support.deprecation = :stderr
48 |
49 | # Raise exceptions for disallowed deprecations.
50 | config.active_support.disallowed_deprecation = :raise
51 |
52 | # Tell Active Support which deprecation messages to disallow.
53 | config.active_support.disallowed_deprecation_warnings = []
54 |
55 | # Raises error for missing translations.
56 | # config.i18n.raise_on_missing_translations = true
57 |
58 | # Annotate rendered view with file names.
59 | # config.action_view.annotate_rendered_view_with_filenames = true
60 | end
61 |
--------------------------------------------------------------------------------
/app/javascript/controllers/address_controller.js:
--------------------------------------------------------------------------------
1 | import {Controller} from '@hotwired/stimulus'
2 |
3 | export default class extends Controller {
4 | static targets = [
5 | 'input',
6 | 'line1',
7 | 'line2',
8 | 'city',
9 | 'state',
10 | 'postal_code',
11 | 'country',
12 | 'lat',
13 | 'lng',
14 | ]
15 |
16 | connect() {
17 | console.log("Address controller is connected");
18 | if(window.google) {
19 | this.initGoogle();
20 | }
21 | }
22 |
23 | initGoogle() {
24 | // setup autocomplete
25 | console.log(`Google maps is initialized and the address controller knows about it`)
26 | console.log(google);
27 | this.autocomplete = new google.maps.places.Autocomplete(this.inputTarget, {
28 | fields: ["address_components", "geometry"],
29 | types: ["address"],
30 | })
31 | this.autocomplete.addListener('place_changed', this.placeSelected.bind(this))
32 | }
33 |
34 | placeSelected() {
35 | // use values from autocomplete
36 | const place = this.autocomplete.getPlace();
37 |
38 | if(!place.geometry) {
39 | return;
40 | }
41 |
42 | this.latTarget.value = place.geometry.location.lat();
43 | this.lngTarget.value = place.geometry.location.lng();
44 |
45 |
46 | // Get each component of the address from the place details,
47 | // and then fill-in the corresponding field on the form.
48 | // place.address_components are google.maps.GeocoderAddressComponent objects
49 | // which are documented at http://goo.gle/3l5i5Mr
50 | for (const component of place.address_components) {
51 | const componentType = component.types[0];
52 |
53 | switch (componentType) {
54 | case "street_number": {
55 | this.line1Target.value = `${component.long_name} ${this.line1Target.value}`;
56 | break;
57 | }
58 |
59 | case "route": {
60 | this.line1Target.value += component.short_name;
61 | break;
62 | }
63 |
64 | case "postal_code": {
65 | this.postal_codeTarget.value = `${component.long_name}${this.postal_codeTarget.value}`;
66 | break;
67 | }
68 |
69 | case "postal_code_suffix": {
70 | this.postal_codeTarget.value = `${this.postal_codeTarget.value}-${component.long_name}`;
71 | break;
72 | }
73 | case "locality":
74 | this.cityTarget.value = component.long_name;
75 | break;
76 | case "administrative_area_level_1": {
77 | this.stateTarget.value = component.short_name;
78 | break;
79 | }
80 | case "country":
81 | this.countryTarget.value = component.short_name;
82 | break;
83 | }
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/config/environments/development.rb:
--------------------------------------------------------------------------------
1 | require "active_support/core_ext/integer/time"
2 |
3 | Rails.application.configure do
4 | # Settings specified here will take precedence over those in config/application.rb.
5 |
6 | # In the development environment your application's code is reloaded any time
7 | # it changes. This slows down response time but is perfect for development
8 | # since you don't have to restart the web server when you make code changes.
9 | config.cache_classes = false
10 |
11 | # Do not eager load code on boot.
12 | config.eager_load = false
13 |
14 | # Show full error reports.
15 | config.consider_all_requests_local = true
16 |
17 | # Enable/disable caching. By default caching is disabled.
18 | # Run rails dev:cache to toggle caching.
19 | if Rails.root.join("tmp/caching-dev.txt").exist?
20 | config.action_controller.perform_caching = true
21 | config.action_controller.enable_fragment_cache_logging = true
22 |
23 | config.cache_store = :memory_store
24 | config.public_file_server.headers = {
25 | "Cache-Control" => "public, max-age=#{2.days.to_i}"
26 | }
27 | else
28 | config.action_controller.perform_caching = false
29 |
30 | config.cache_store = :null_store
31 | end
32 |
33 | # Store uploaded files on the local file system (see config/storage.yml for options).
34 | config.active_storage.service = :local
35 |
36 | # Don't care if the mailer can't send.
37 | config.action_mailer.raise_delivery_errors = false
38 | config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
39 | config.action_mailer.delivery_method = :letter_opener
40 | config.action_mailer.perform_deliveries = true
41 |
42 | config.action_mailer.perform_caching = false
43 |
44 | # Print deprecation notices to the Rails logger.
45 | config.active_support.deprecation = :log
46 |
47 | # Raise exceptions for disallowed deprecations.
48 | config.active_support.disallowed_deprecation = :raise
49 |
50 | # Tell Active Support which deprecation messages to disallow.
51 | config.active_support.disallowed_deprecation_warnings = []
52 |
53 | # Raise an error on page load if there are pending migrations.
54 | config.active_record.migration_error = :page_load
55 |
56 | # Highlight code that triggered database queries in logs.
57 | config.active_record.verbose_query_logs = true
58 |
59 | # Suppress logger output for asset requests.
60 | config.assets.quiet = true
61 |
62 | # Raises error for missing translations.
63 | # config.i18n.raise_on_missing_translations = true
64 |
65 | # Annotate rendered view with file names.
66 | # config.action_view.annotate_rendered_view_with_filenames = true
67 |
68 | # Uncomment if you wish to allow Action Cable access from any origin.
69 | # config.action_cable.disable_request_forgery_protection = true
70 | end
71 |
--------------------------------------------------------------------------------
/app/views/messages/index.html.erb:
--------------------------------------------------------------------------------
1 | Messages
2 |
3 |
4 |
5 | <% @messages.each_with_index do |message, i| %>
6 |
7 |
8 | <% if i < @messages.length - 1 %>
9 |
10 | <% end %>
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
28 |
Sent <%= time_ago_in_words(message.created_at) %> ago
29 |
30 |
31 |
<%= message.content %>
32 |
33 |
34 | <% if message&.notifications_as_message&.first&.read_at %>
35 |
Read <%= time_ago_in_words(message&.notifications_as_message&.first&.read_at) %>
36 | <% end %>
37 |
38 |
39 |
40 |
41 |
42 | <% end %>
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
Add your message
51 |
52 |
53 |
54 |
55 |
56 | Send
57 |
58 |
59 |
--------------------------------------------------------------------------------
/app/jobs/event_job.rb:
--------------------------------------------------------------------------------
1 | class EventJob < ApplicationJob
2 | queue_as :default
3 |
4 | def perform(event)
5 | case event.source
6 | when "stripe"
7 | stripe_event = Stripe::Event.construct_from(
8 | JSON.parse(event.request_body, symbolize_names: true)
9 | )
10 | begin
11 | handle_stripe(stripe_event)
12 | event.update(
13 | event_type: stripe_event.type,
14 | error_message: '',
15 | status: :processed
16 | )
17 | rescue => e
18 | event.update(
19 | error_message: e.message,
20 | status: :failed
21 | )
22 | end
23 | else
24 | event.update(
25 | error_message: "Unknown source #{event.source}",
26 | status: :failed
27 | )
28 | end
29 | end
30 |
31 | def handle_stripe(event)
32 | case event.type
33 | when "account.updated"
34 | account = event.data.object
35 | user = User.find_by(stripe_account_id: account.id)
36 | user.update(charges_enabled: account.charges_enabled)
37 | when "checkout.session.completed"
38 | # do something with the checkout session and the reservation here.
39 | checkout_session = event.data.object
40 | reservation = Reservation.find_by(session_id: checkout_session.id)
41 | if reservation.nil?
42 | raise "No reservation found with Checkout Session ID #{checkout_session.id}"
43 | end
44 | reservation.update(status: :booked, stripe_payment_intent_id: checkout_session.payment_intent)
45 | HostReservationBookedNotification.with(reservation: reservation, message: "New Booking! #{reservation.guest.email} is coming #{reservation.start_date}").deliver_later(reservation.host)
46 | GuestReservationBookedNotification.with(reservation: reservation).deliver_later(reservation.guest)
47 | when "checkout.session.expired"
48 | checkout_session = event.data.object
49 | reservation = Reservation.find_by(session_id: checkout_session.id)
50 | if reservation.nil?
51 | raise "No reservation found with Checkout Session ID #{checkout_session.id}"
52 | end
53 | reservation.update(status: :expired)
54 | when "charge.refunded"
55 | charge = event.data.object
56 | reservation = Reservation.find_by(stripe_payment_intent_id: charge.payment_intent)
57 | if reservation.nil?
58 | raise "No reservation found with Payment Intent ID #{charge.payment_intent}"
59 | end
60 | reservation.update(status: :cancelled)
61 | when "identity.verification_session.verified"
62 | session = event.data.object
63 | user = User.find_by(id: session.metadata.user_id)
64 | if user.nil?
65 | raise "No user found with ID #{session.metadata.user_id}"
66 | end
67 | if session.status == "verified"
68 | user.update(identity_verified: true)
69 | else
70 | user.update(identity_verified: false)
71 | end
72 | end
73 | end
74 | end
75 |
--------------------------------------------------------------------------------
/app/models/user.rb:
--------------------------------------------------------------------------------
1 | # == Schema Information
2 | #
3 | # Table name: users
4 | #
5 | # id :bigint not null, primary key
6 | # created_at :datetime not null
7 | # updated_at :datetime not null
8 | # email :string default(""), not null
9 | # encrypted_password :string default(""), not null
10 | # reset_password_token :string
11 | # reset_password_sent_at :datetime
12 | # remember_created_at :datetime
13 | # sign_in_count :integer default(0), not null
14 | # current_sign_in_at :datetime
15 | # last_sign_in_at :datetime
16 | # current_sign_in_ip :string
17 | # last_sign_in_ip :string
18 | # confirmation_token :string
19 | # confirmed_at :datetime
20 | # confirmation_sent_at :datetime
21 | # unconfirmed_email :string
22 | # failed_attempts :integer default(0), not null
23 | # unlock_token :string
24 | # locked_at :datetime
25 | # provider :string
26 | # uid :string
27 | # name :string
28 | # stripe_customer_id :string
29 | # is_host :boolean default(FALSE)
30 | # stripe_account_id :string
31 | # charges_enabled :boolean default(FALSE)
32 | #
33 | class User < ApplicationRecord
34 | devise :database_authenticatable, :registerable,
35 | :recoverable, :rememberable, :validatable,
36 | :confirmable, :lockable, :timeoutable, :trackable,
37 | :omniauthable, omniauth_providers: [:google]
38 | has_many :listings, foreign_key: :host_id
39 | has_many :reservations, foreign_key: :guest_id
40 | has_many :host_reservations, class_name: 'Reservation', through: :listings, source: :reservations
41 | has_many :notifications, as: :recipient
42 | has_many :sent_messages, class_name: 'Message', foreign_key: 'from_user_id'
43 | has_many :received_messages, class_name: 'Message', foreign_key: 'to_user_id'
44 |
45 | after_commit :maybe_create_stripe_customer, on: [:create, :update]
46 |
47 | def all_reservations
48 | Reservation.where(guest: self).or(Reservation.where(listing: listings))
49 | end
50 |
51 | def maybe_create_stripe_customer
52 | return if !stripe_customer_id.blank?
53 |
54 | customer = Stripe::Customer.create(
55 | email: email,
56 | name: name,
57 | metadata: {
58 | clearbnb_id: id
59 | }
60 | )
61 | update(stripe_customer_id: customer.id)
62 | end
63 |
64 | def self.from_omniauth(auth)
65 | where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
66 | user.email = auth.info.email
67 | user.password = Devise.friendly_token[0, 20]
68 | user.name = auth.info.name # assuming the user model has a name
69 | # user.image = auth.info.image # assuming the user model has an image
70 | # If you are using confirmable and the provider(s) you use validate emails,
71 | # uncomment the line below to skip the confirmation emails.
72 | user.skip_confirmation!
73 | end
74 | end
75 | end
76 |
--------------------------------------------------------------------------------
/app/views/host/listings/new.html.erb:
--------------------------------------------------------------------------------
1 | List your property
2 |
3 | <%= render partial: "shared/errors" %>
4 |
5 |
6 |
7 |
8 |
9 |
10 | Title
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | About
20 |
21 | <%= @listing.about %>
22 |
23 |
24 |
25 |
26 | Address
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | How many people maxium can stay?
43 |
44 |
45 |
46 |
47 | Nightly price
48 |
49 |
50 | Cleaning fee
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/app/models/book_listing.rb:
--------------------------------------------------------------------------------
1 | class BookListing
2 | include Rails.application.routes.url_helpers
3 |
4 | attr_reader :current_user, :params
5 |
6 | def initialize(current_user, params)
7 | @current_user = current_user
8 | @params = params
9 | end
10 |
11 | def save
12 | if valid?
13 | calendar_event.save && reservation.save
14 | end
15 | end
16 |
17 | def valid?
18 | calendar_event.valid? || reservation.valid?
19 | end
20 |
21 | def errors
22 | calendar_event.errors.full_messages + reservation.errors.full_messages
23 | end
24 |
25 | def checkout_url
26 | checkout_session.url
27 | end
28 |
29 | def checkout_session
30 | return @checkout_session if @checkout_session
31 |
32 | # Create a Stripe Checkout session
33 | @checkout_session = Stripe::Checkout::Session.create(
34 | success_url: reservation_url(reservation),
35 | cancel_url: expire_reservation_url(reservation) + "?session_id={CHECKOUT_SESSION_ID}",
36 | customer: current_user.stripe_customer_id,
37 | mode: 'payment',
38 | allow_promotion_codes: true,
39 | expires_at: 1.hour.from_now.to_i,
40 | submit_type: 'book',
41 | line_items: [{
42 | price_data: {
43 | unit_amount: listing.nightly_price,
44 | currency: 'usd',
45 | product: listing.stripe_product_id,
46 | },
47 | quantity: reservation.nights, # Num nights
48 | }, {
49 | price_data: {
50 | unit_amount: listing.cleaning_fee,
51 | currency: 'usd',
52 | product: 'prod_KfZvqsikciZVFr', # Cleaning fee product ID
53 | },
54 | quantity: 1
55 | }],
56 | metadata: {
57 | listing_id: listing.id,
58 | reservation_id: reservation.id,
59 | guest_id: current_user.id,
60 | start_date: start_date,
61 | end_date: end_date,
62 | },
63 | payment_intent_data: {
64 | application_fee_amount: ((listing.cleaning_fee + listing.nightly_price) * 0.10).to_i,
65 | transfer_data: {
66 | destination: listing.host.stripe_account_id
67 | },
68 | metadata: {
69 | reservation_id: reservation.id,
70 | listing_id: listing.id,
71 | reservation_id: reservation.id,
72 | guest_id: current_user.id,
73 | start_date: start_date,
74 | end_date: end_date,
75 | }
76 | }
77 | )
78 | reservation.update(session_id: @checkout_session.id)
79 | checkout_session
80 | end
81 |
82 | def calendar_event
83 | @calendar_event ||= CalendarEvent.new(
84 | listing: listing,
85 | reservation: reservation,
86 | status: :reserved,
87 | start_date: start_date,
88 | end_date: end_date,
89 | )
90 | end
91 |
92 | def reservation
93 | @reservation ||= Reservation.new(
94 | listing: listing,
95 | guest: current_user,
96 | )
97 | end
98 |
99 | def listing
100 | @listing ||= Listing.find(listing_id)
101 | end
102 |
103 | private
104 |
105 | def start_date
106 | params[:start_date]
107 | end
108 |
109 | def end_date
110 | params[:end_date]
111 | end
112 |
113 | def listing_id
114 | params[:listing_id]
115 | end
116 | end
117 |
--------------------------------------------------------------------------------
/config/database.yml:
--------------------------------------------------------------------------------
1 | # PostgreSQL. Versions 9.3 and up are supported.
2 | #
3 | # Install the pg driver:
4 | # gem install pg
5 | # On macOS with Homebrew:
6 | # gem install pg -- --with-pg-config=/usr/local/bin/pg_config
7 | # On macOS with MacPorts:
8 | # gem install pg -- --with-pg-config=/opt/local/lib/postgresql84/bin/pg_config
9 | # On Windows:
10 | # gem install pg
11 | # Choose the win32 build.
12 | # Install PostgreSQL and put its /bin directory on your path.
13 | #
14 | # Configure Using Gemfile
15 | # gem 'pg'
16 | #
17 | default: &default
18 | adapter: postgresql
19 | encoding: unicode
20 | # For details on connection pooling, see Rails configuration guide
21 | # https://guides.rubyonrails.org/configuring.html#database-pooling
22 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
23 |
24 | development:
25 | <<: *default
26 | database: clearbnb_development
27 |
28 | # The specified database role being used to connect to postgres.
29 | # To create additional roles in postgres see `$ createuser --help`.
30 | # When left blank, postgres will use the default role. This is
31 | # the same name as the operating system user running Rails.
32 | #username: clearbnb
33 |
34 | # The password associated with the postgres role (username).
35 | #password:
36 |
37 | # Connect on a TCP socket. Omitted by default since the client uses a
38 | # domain socket that doesn't need configuration. Windows does not have
39 | # domain sockets, so uncomment these lines.
40 | #host: localhost
41 |
42 | # The TCP port the server listens on. Defaults to 5432.
43 | # If your server runs on a different port number, change accordingly.
44 | #port: 5432
45 |
46 | # Schema search path. The server defaults to $user,public
47 | #schema_search_path: myapp,sharedapp,public
48 |
49 | # Minimum log levels, in increasing order:
50 | # debug5, debug4, debug3, debug2, debug1,
51 | # log, notice, warning, error, fatal, and panic
52 | # Defaults to warning.
53 | #min_messages: notice
54 |
55 | # Warning: The database defined as "test" will be erased and
56 | # re-generated from your development database when you run "rake".
57 | # Do not set this db to the same as development or production.
58 | test:
59 | <<: *default
60 | database: clearbnb_test
61 |
62 | # As with config/credentials.yml, you never want to store sensitive information,
63 | # like your database password, in your source code. If your source code is
64 | # ever seen by anyone, they now have access to your database.
65 | #
66 | # Instead, provide the password or a full connection URL as an environment
67 | # variable when you boot the app. For example:
68 | #
69 | # DATABASE_URL="postgres://myuser:mypass@localhost/somedatabase"
70 | #
71 | # If the connection URL is provided in the special DATABASE_URL environment
72 | # variable, Rails will automatically merge its configuration values on top of
73 | # the values provided in this file. Alternatively, you can specify a connection
74 | # URL environment variable explicitly:
75 | #
76 | # production:
77 | # url: <%= ENV['MY_APP_DATABASE_URL'] %>
78 | #
79 | # Read https://guides.rubyonrails.org/configuring.html#configuring-a-database
80 | # for a full overview on how database connection configuration can be specified.
81 | #
82 | production:
83 | <<: *default
84 | database: clearbnb_production
85 | username: clearbnb
86 | password: <%= ENV['CLEARBNB_DATABASE_PASSWORD'] %>
87 |
--------------------------------------------------------------------------------
/bin/bundle:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'bundle' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "rubygems"
12 |
13 | m = Module.new do
14 | module_function
15 |
16 | def invoked_as_script?
17 | File.expand_path($0) == File.expand_path(__FILE__)
18 | end
19 |
20 | def env_var_version
21 | ENV["BUNDLER_VERSION"]
22 | end
23 |
24 | def cli_arg_version
25 | return unless invoked_as_script? # don't want to hijack other binstubs
26 | return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update`
27 | bundler_version = nil
28 | update_index = nil
29 | ARGV.each_with_index do |a, i|
30 | if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN
31 | bundler_version = a
32 | end
33 | next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/
34 | bundler_version = $1
35 | update_index = i
36 | end
37 | bundler_version
38 | end
39 |
40 | def gemfile
41 | gemfile = ENV["BUNDLE_GEMFILE"]
42 | return gemfile if gemfile && !gemfile.empty?
43 |
44 | File.expand_path("../../Gemfile", __FILE__)
45 | end
46 |
47 | def lockfile
48 | lockfile =
49 | case File.basename(gemfile)
50 | when "gems.rb" then gemfile.sub(/\.rb$/, gemfile)
51 | else "#{gemfile}.lock"
52 | end
53 | File.expand_path(lockfile)
54 | end
55 |
56 | def lockfile_version
57 | return unless File.file?(lockfile)
58 | lockfile_contents = File.read(lockfile)
59 | return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/
60 | Regexp.last_match(1)
61 | end
62 |
63 | def bundler_version
64 | @bundler_version ||=
65 | env_var_version || cli_arg_version ||
66 | lockfile_version
67 | end
68 |
69 | def bundler_requirement
70 | return "#{Gem::Requirement.default}.a" unless bundler_version
71 |
72 | bundler_gem_version = Gem::Version.new(bundler_version)
73 |
74 | requirement = bundler_gem_version.approximate_recommendation
75 |
76 | return requirement unless Gem::Version.new(Gem::VERSION) < Gem::Version.new("2.7.0")
77 |
78 | requirement += ".a" if bundler_gem_version.prerelease?
79 |
80 | requirement
81 | end
82 |
83 | def load_bundler!
84 | ENV["BUNDLE_GEMFILE"] ||= gemfile
85 |
86 | activate_bundler
87 | end
88 |
89 | def activate_bundler
90 | gem_error = activation_error_handling do
91 | gem "bundler", bundler_requirement
92 | end
93 | return if gem_error.nil?
94 | require_error = activation_error_handling do
95 | require "bundler/version"
96 | end
97 | return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION))
98 | warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`"
99 | exit 42
100 | end
101 |
102 | def activation_error_handling
103 | yield
104 | nil
105 | rescue StandardError, LoadError => e
106 | e
107 | end
108 | end
109 |
110 | m.load_bundler!
111 |
112 | if m.invoked_as_script?
113 | load Gem.bin_path("bundler", "bundle")
114 | end
115 |
--------------------------------------------------------------------------------
/app/views/host/listings/index.html.erb:
--------------------------------------------------------------------------------
1 | Your listings
2 |
3 |
4 | Add a new listing
5 |
6 |
7 |
8 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/config/locales/devise.en.yml:
--------------------------------------------------------------------------------
1 | # Additional translations at https://github.com/heartcombo/devise/wiki/I18n
2 |
3 | en:
4 | devise:
5 | confirmations:
6 | confirmed: "Your email address has been successfully confirmed."
7 | send_instructions: "You will receive an email with instructions for how to confirm your email address in a few minutes."
8 | 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."
9 | failure:
10 | already_authenticated: "You are already signed in."
11 | inactive: "Your account is not activated yet."
12 | invalid: "Invalid %{authentication_keys} or password."
13 | locked: "Your account is locked."
14 | last_attempt: "You have one more attempt before your account is locked."
15 | not_found_in_database: "Invalid %{authentication_keys} or password."
16 | timeout: "Your session expired. Please sign in again to continue."
17 | unauthenticated: "You need to sign in or sign up before continuing."
18 | unconfirmed: "You have to confirm your email address before continuing."
19 | mailer:
20 | confirmation_instructions:
21 | subject: "Confirmation instructions"
22 | reset_password_instructions:
23 | subject: "Reset password instructions"
24 | unlock_instructions:
25 | subject: "Unlock instructions"
26 | email_changed:
27 | subject: "Email Changed"
28 | password_change:
29 | subject: "Password Changed"
30 | omniauth_callbacks:
31 | failure: "Could not authenticate you from %{kind} because \"%{reason}\"."
32 | success: "Successfully authenticated from %{kind} account."
33 | passwords:
34 | 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."
35 | send_instructions: "You will receive an email with instructions on how to reset your password in a few minutes."
36 | 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."
37 | updated: "Your password has been changed successfully. You are now signed in."
38 | updated_not_active: "Your password has been changed successfully."
39 | registrations:
40 | destroyed: "Bye! Your account has been successfully cancelled. We hope to see you again soon."
41 | signed_up: "Welcome! You have signed up successfully."
42 | signed_up_but_inactive: "You have signed up successfully. However, we could not sign you in because your account is not yet activated."
43 | signed_up_but_locked: "You have signed up successfully. However, we could not sign you in because your account is locked."
44 | 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."
45 | 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."
46 | updated: "Your account has been updated successfully."
47 | updated_but_not_signed_in: "Your account has been updated successfully, but since your password was changed, you need to sign in again."
48 | sessions:
49 | signed_in: "Signed in successfully."
50 | signed_out: "Signed out successfully."
51 | already_signed_out: "Signed out successfully."
52 | unlocks:
53 | send_instructions: "You will receive an email with instructions for how to unlock your account in a few minutes."
54 | send_paranoid_instructions: "If your account exists, you will receive an email with instructions for how to unlock it in a few minutes."
55 | unlocked: "Your account has been unlocked successfully. Please sign in to continue."
56 | errors:
57 | messages:
58 | already_confirmed: "was already confirmed, please try signing in"
59 | confirmation_period_expired: "needs to be confirmed within %{period}, please request a new one"
60 | expired: "has expired, please request a new one"
61 | not_found: "not found"
62 | not_locked: "was not locked"
63 | not_saved:
64 | one: "1 error prohibited this %{resource} from being saved:"
65 | other: "%{count} errors prohibited this %{resource} from being saved:"
66 |
--------------------------------------------------------------------------------
/config/initializers/new_framework_defaults_7_0.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 | #
3 | # This file eases your Rails 7.0 framework defaults upgrade.
4 | #
5 | # Uncomment each configuration one by one to switch to the new default.
6 | # Once your application is ready to run with all new defaults, you can remove
7 | # this file and set the `config.load_defaults` to `7.0`.
8 | #
9 | # Read the Guide for Upgrading Ruby on Rails for more info on each option.
10 | # https://guides.rubyonrails.org/upgrading_ruby_on_rails.html
11 |
12 | # Raise an error when trying to use forgery protection without a working
13 | # session.
14 | # Rails.application.config.action_controller.silence_disabled_session_errors = false
15 |
16 | # `button_to` view helper will render `` element, regardless of whether
17 | # or not the content is passed as the first argument or as a block.
18 | # Rails.application.config.action_view.button_to_generates_button_tag = true
19 |
20 | # `stylesheet_link_tag` view helper will not render the media attribute by default.
21 | # Rails.application.config.action_view.apply_stylesheet_media_default = false
22 |
23 | # Change the digest class for the key generators to `OpenSSL::Digest::SHA256`.
24 | # Changing this default means invalidate all encrypted messages generated by
25 | # your application and, all the encrypted cookies. Only change this after you
26 | # rotated all the messages using the key rotator.
27 | #
28 | # See upgrading guide for more information on how to build a rotator.
29 | # https://guides.rubyonrails.org/v7.0/upgrading_ruby_on_rails.html
30 | # Rails.application.config.active_support.key_generator_hash_digest_class = OpenSSL::Digest::SHA256
31 |
32 | # Change the digest class for ActiveSupport::Digest.
33 | # Changing this default means that for example Etags change and
34 | # various cache keys leading to cache invalidation.
35 | # Rails.application.config.active_support.hash_digest_class = OpenSSL::Digest::SHA256
36 |
37 | # Don't override ActiveSupport::TimeWithZone.name and use the default Ruby
38 | # implementation.
39 | # Rails.application.config.active_support.remove_deprecated_time_with_zone_name = true
40 |
41 | # Change the format of the cache entry.
42 | # Changing this default means that all new cache entries added to the cache
43 | # will have a different format that is not supported by Rails 6.1 applications.
44 | # Only change this value after your application is fully deployed to Rails 7.0
45 | # and you have no plans to rollback.
46 | # Rails.application.config.active_support.cache_format_version = 7.0
47 |
48 | # Set both the `:open_timeout` and `:read_timeout` values for `:smtp` delivery method.
49 | # Rails.application.config.action_mailer.smtp_timeout = 5
50 |
51 | # The ActiveStorage video previewer will now use scene change detection to generate
52 | # better preview images (rather than the previous default of using the first frame
53 | # of the video).
54 | # Rails.application.config.active_storage.video_preview_arguments =
55 | # "-vf 'select=eq(n\\,0)+eq(key\\,1)+gt(scene\\,0.015),loop=loop=-1:size=2,trim=start_frame=1' -frames:v 1 -f image2"
56 |
57 | # Raise when running tests if fixtures contained foreign key violations
58 | # Rails.application.config.active_record.verify_foreign_keys_for_fixtures = true
59 |
60 | # Disable partial inserts.
61 | # This default means that all columns will be referenced in INSERT queries
62 | # regardless of whether they have a default or not.
63 | # Rails.application.config.active_record.partial_inserts = false
64 | #
65 | # Protect from open redirect attacks in `redirect_back_or_to` and `redirect_to`.
66 | # Rails.application.config.action_controller.raise_on_open_redirects = true
67 |
68 | # Change the variant processor for Active Storage.
69 | # Changing this default means updating all places in your code that
70 | # generate variants to use image processing macros and ruby-vips
71 | # operations. See the upgrading guide for detail on the changes required.
72 | # The `:mini_magick` option is not deprecated; it's fine to keep using it.
73 | # Rails.application.config.active_storage.variant_processor = :vips
74 |
75 | # If you're upgrading and haven't set `cookies_serializer` previously, your cookie serializer
76 | # was `:marshal`. Convert all cookies to JSON, using the `:hybrid` formatter.
77 | #
78 | # If you're confident all your cookies are JSON formatted, you can switch to the `:json` formatter.
79 | #
80 | # Continue to use `:marshal` for backward-compatibility with old cookies.
81 | #
82 | # If you have configured the serializer elsewhere, you can remove this.
83 | #
84 | # See https://guides.rubyonrails.org/action_controller_overview.html#cookies for more information.
85 | # Rails.application.config.action_dispatch.cookies_serializer = :hybrid
86 |
--------------------------------------------------------------------------------
/app/views/devise/registrations/new.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
9 |
10 |
11 | <%= render "devise/shared/error_messages", resource: resource %>
12 |
13 |
14 |
15 |
16 |
17 | Register with
18 |
19 |
20 |
28 |
29 |
30 |
31 |
34 |
35 |
36 | Or continue with
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | Email address
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | Password
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | Password confirmation
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | Register
76 |
77 |
78 |
79 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
--------------------------------------------------------------------------------
/app/views/devise/sessions/new.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
9 |
10 | <%= render "devise/shared/error_messages", resource: resource %>
11 | <%= render "shared/notices" %>
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | Sign in with
20 |
21 |
22 |
30 |
31 |
32 |
33 |
36 |
37 |
38 | Or continue with
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | Email address
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | Password
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | <% if devise_mapping.rememberable? %>
67 |
68 |
69 |
70 | Remember me
71 |
72 |
73 | <% end %>
74 |
75 |
76 |
77 |
78 | Sign in
79 |
80 |
81 |
82 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
--------------------------------------------------------------------------------
/config/environments/production.rb:
--------------------------------------------------------------------------------
1 | require "active_support/core_ext/integer/time"
2 |
3 | Rails.application.configure do
4 | # Settings specified here will take precedence over those in config/application.rb.
5 |
6 | # Code is not reloaded between requests.
7 | config.cache_classes = true
8 |
9 | # Eager load code on boot. This eager loads most of Rails and
10 | # your application in memory, allowing both threaded web servers
11 | # and those relying on copy on write to perform better.
12 | # Rake tasks automatically ignore this option for performance.
13 | config.eager_load = true
14 |
15 | # Full error reports are disabled and caching is turned on.
16 | config.consider_all_requests_local = false
17 | config.action_controller.perform_caching = true
18 |
19 | # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
20 | # or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
21 | # config.require_master_key = true
22 |
23 | # Disable serving static files from the `/public` folder by default since
24 | # Apache or NGINX already handles this.
25 | config.public_file_server.enabled = ENV["RAILS_SERVE_STATIC_FILES"].present?
26 |
27 | # Compress CSS using a preprocessor.
28 | # config.assets.css_compressor = :sass
29 |
30 | # Do not fallback to assets pipeline if a precompiled asset is missed.
31 | config.assets.compile = false
32 |
33 | # Enable serving of images, stylesheets, and JavaScripts from an asset server.
34 | # config.asset_host = "http://assets.example.com"
35 |
36 | # Specifies the header that your server uses for sending files.
37 | # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for Apache
38 | # config.action_dispatch.x_sendfile_header = "X-Accel-Redirect" # for NGINX
39 |
40 | # Store uploaded files on the local file system (see config/storage.yml for options).
41 | config.active_storage.service = :local
42 |
43 | # Mount Action Cable outside main process or domain.
44 | # config.action_cable.mount_path = nil
45 | # config.action_cable.url = "wss://example.com/cable"
46 | # config.action_cable.allowed_request_origins = [ "http://example.com", /http:\/\/example.*/ ]
47 |
48 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
49 | # config.force_ssl = true
50 |
51 | # Include generic and useful information about system operation, but avoid logging too much
52 | # information to avoid inadvertent exposure of personally identifiable information (PII).
53 | config.log_level = :info
54 |
55 | # Prepend all log lines with the following tags.
56 | config.log_tags = [ :request_id ]
57 |
58 | # Use a different cache store in production.
59 | # config.cache_store = :mem_cache_store
60 |
61 | # Use a real queuing backend for Active Job (and separate queues per environment).
62 | # config.active_job.queue_adapter = :resque
63 | # config.active_job.queue_name_prefix = "clearbnb_production"
64 |
65 | config.action_mailer.perform_caching = false
66 |
67 | # Ignore bad email addresses and do not raise email delivery errors.
68 | # Set this to true and configure the email server for immediate delivery to raise delivery errors.
69 | # config.action_mailer.raise_delivery_errors = false
70 |
71 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
72 | # the I18n.default_locale when a translation cannot be found).
73 | config.i18n.fallbacks = true
74 |
75 | # Don't log any deprecations.
76 | config.active_support.report_deprecations = false
77 |
78 | # Use default logging formatter so that PID and timestamp are not suppressed.
79 | config.log_formatter = ::Logger::Formatter.new
80 |
81 | # Use a different logger for distributed setups.
82 | # require "syslog/logger"
83 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new "app-name")
84 |
85 | if ENV["RAILS_LOG_TO_STDOUT"].present?
86 | logger = ActiveSupport::Logger.new(STDOUT)
87 | logger.formatter = config.log_formatter
88 | config.logger = ActiveSupport::TaggedLogging.new(logger)
89 | end
90 |
91 | # Do not dump schema after migrations.
92 | config.active_record.dump_schema_after_migration = false
93 |
94 | # Inserts middleware to perform automatic connection switching.
95 | # The `database_selector` hash is used to pass options to the DatabaseSelector
96 | # middleware. The `delay` is used to determine how long to wait after a write
97 | # to send a subsequent read to the primary.
98 | #
99 | # The `database_resolver` class is used by the middleware to determine which
100 | # database is appropriate to use based on the time delay.
101 | #
102 | # The `database_resolver_context` class is used by the middleware to set
103 | # timestamps for the last write to the primary. The resolver uses the context
104 | # class timestamps to determine how long to wait before reading from the
105 | # replica.
106 | #
107 | # By default Rails will store a last write timestamp in the session. The
108 | # DatabaseSelector middleware is designed as such you can define your own
109 | # strategy for connection switching and pass that into the middleware through
110 | # these configuration options.
111 | # config.active_record.database_selector = { delay: 2.seconds }
112 | # config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
113 | # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
114 | end
115 |
--------------------------------------------------------------------------------
/app/views/reservations/index.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Your upcoming trips
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Listing
15 | Check in
16 | Check out
17 | Status
18 |
19 | View
20 |
21 |
22 |
23 |
24 | <% @reservations.each do |reservation| %>
25 |
26 |
27 | <%= link_to reservation.listing.title, reservation_path(reservation) %>
28 |
29 |
30 | <%= reservation.start_date %>
31 |
32 |
33 | <%= reservation.end_date %>
34 |
35 |
36 | <%= reservation.status %>
37 |
38 |
39 | View, Lindsay Walton
40 |
41 |
42 | <% end %>
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | <% if !@host_reservations.empty? %>
54 |
55 |
56 |
57 |
You are hosting these guests
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | Listing
68 | Guest
69 | Check in
70 | Check out
71 | Status
72 |
73 | View
74 |
75 |
76 |
77 |
78 | <% @host_reservations.each do |reservation| %>
79 |
80 |
81 | <%= link_to reservation.listing.title, host_reservation_path(reservation) %>
82 |
83 |
84 | <%= reservation.guest.email %>
85 |
86 |
87 | <%= reservation.start_date %>
88 |
89 |
90 | <%= reservation.end_date %>
91 |
92 |
93 | <%= reservation.status %>
94 |
95 |
96 | View, Lindsay Walton
97 |
98 |
99 | <% end %>
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 | <% end %>
108 |
--------------------------------------------------------------------------------
/db/schema.rb:
--------------------------------------------------------------------------------
1 | # This file is auto-generated from the current state of the database. Instead
2 | # of editing this file, please use the migrations feature of Active Record to
3 | # incrementally modify your database, and then regenerate this schema definition.
4 | #
5 | # This file is the source Rails uses to define your schema when running `bin/rails
6 | # db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
7 | # be faster and is potentially less error prone than running all of your
8 | # migrations from scratch. Old migrations may fail to apply correctly if those
9 | # migrations use external dependencies or application code.
10 | #
11 | # It's strongly recommended that you check this file into your version control system.
12 |
13 | ActiveRecord::Schema.define(version: 2022_03_17_151520) do
14 |
15 | # These are extensions that must be enabled in order to support this database
16 | enable_extension "plpgsql"
17 |
18 | create_table "active_storage_attachments", force: :cascade do |t|
19 | t.string "name", null: false
20 | t.string "record_type", null: false
21 | t.bigint "record_id", null: false
22 | t.bigint "blob_id", null: false
23 | t.datetime "created_at", precision: 6, null: false
24 | t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id"
25 | t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
26 | end
27 |
28 | create_table "active_storage_blobs", force: :cascade do |t|
29 | t.string "key", null: false
30 | t.string "filename", null: false
31 | t.string "content_type"
32 | t.text "metadata"
33 | t.string "service_name", null: false
34 | t.bigint "byte_size", null: false
35 | t.string "checksum", null: false
36 | t.datetime "created_at", precision: 6, null: false
37 | t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true
38 | end
39 |
40 | create_table "active_storage_variant_records", force: :cascade do |t|
41 | t.bigint "blob_id", null: false
42 | t.string "variation_digest", null: false
43 | t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true
44 | end
45 |
46 | create_table "beds", force: :cascade do |t|
47 | t.bigint "room_id", null: false
48 | t.integer "bed_size"
49 | t.datetime "created_at", precision: 6, null: false
50 | t.datetime "updated_at", precision: 6, null: false
51 | t.index ["room_id"], name: "index_beds_on_room_id"
52 | end
53 |
54 | create_table "calendar_events", force: :cascade do |t|
55 | t.bigint "listing_id", null: false
56 | t.bigint "reservation_id"
57 | t.integer "status", null: false
58 | t.date "start_date", null: false
59 | t.date "end_date", null: false
60 | t.datetime "created_at", precision: 6, null: false
61 | t.datetime "updated_at", precision: 6, null: false
62 | t.index ["listing_id"], name: "index_calendar_events_on_listing_id"
63 | t.index ["reservation_id"], name: "index_calendar_events_on_reservation_id"
64 | end
65 |
66 | create_table "events", force: :cascade do |t|
67 | t.text "request_body"
68 | t.integer "status", default: 0
69 | t.text "error_message"
70 | t.string "source"
71 | t.string "event_type"
72 | t.datetime "created_at", precision: 6, null: false
73 | t.datetime "updated_at", precision: 6, null: false
74 | end
75 |
76 | create_table "listings", force: :cascade do |t|
77 | t.bigint "host_id", null: false
78 | t.string "title", null: false
79 | t.text "about"
80 | t.integer "max_guests", default: 1
81 | t.string "address_line1"
82 | t.string "address_line2"
83 | t.string "city"
84 | t.string "state"
85 | t.string "postal_code"
86 | t.string "country"
87 | t.decimal "lat", precision: 10, scale: 6
88 | t.decimal "lng", precision: 10, scale: 6
89 | t.integer "status", default: 0
90 | t.datetime "created_at", precision: 6, null: false
91 | t.datetime "updated_at", precision: 6, null: false
92 | t.integer "nightly_price"
93 | t.integer "cleaning_fee"
94 | t.string "stripe_product_id"
95 | t.index ["host_id"], name: "index_listings_on_host_id"
96 | end
97 |
98 | create_table "messages", force: :cascade do |t|
99 | t.bigint "from_user_id", null: false
100 | t.bigint "to_user_id", null: false
101 | t.bigint "reservation_id"
102 | t.string "content", default: "", null: false
103 | t.datetime "created_at", precision: 6, null: false
104 | t.datetime "updated_at", precision: 6, null: false
105 | t.index ["from_user_id"], name: "index_messages_on_from_user_id"
106 | t.index ["reservation_id"], name: "index_messages_on_reservation_id"
107 | t.index ["to_user_id"], name: "index_messages_on_to_user_id"
108 | end
109 |
110 | create_table "notifications", force: :cascade do |t|
111 | t.string "recipient_type", null: false
112 | t.bigint "recipient_id", null: false
113 | t.string "type", null: false
114 | t.jsonb "params"
115 | t.datetime "read_at", precision: 6
116 | t.datetime "created_at", precision: 6, null: false
117 | t.datetime "updated_at", precision: 6, null: false
118 | t.index ["read_at"], name: "index_notifications_on_read_at"
119 | t.index ["recipient_type", "recipient_id"], name: "index_notifications_on_recipient"
120 | end
121 |
122 | create_table "photos", force: :cascade do |t|
123 | t.bigint "listing_id", null: false
124 | t.string "caption"
125 | t.datetime "created_at", precision: 6, null: false
126 | t.datetime "updated_at", precision: 6, null: false
127 | t.index ["listing_id"], name: "index_photos_on_listing_id"
128 | end
129 |
130 | create_table "reservations", force: :cascade do |t|
131 | t.bigint "listing_id", null: false
132 | t.bigint "guest_id", null: false
133 | t.string "session_id"
134 | t.integer "status", default: 0
135 | t.datetime "created_at", precision: 6, null: false
136 | t.datetime "updated_at", precision: 6, null: false
137 | t.string "stripe_payment_intent_id"
138 | t.string "stripe_refund_id"
139 | t.index ["guest_id"], name: "index_reservations_on_guest_id"
140 | t.index ["listing_id"], name: "index_reservations_on_listing_id"
141 | end
142 |
143 | create_table "rooms", force: :cascade do |t|
144 | t.bigint "listing_id", null: false
145 | t.integer "room_type"
146 | t.datetime "created_at", precision: 6, null: false
147 | t.datetime "updated_at", precision: 6, null: false
148 | t.index ["listing_id"], name: "index_rooms_on_listing_id"
149 | end
150 |
151 | create_table "users", force: :cascade do |t|
152 | t.datetime "created_at", precision: 6, null: false
153 | t.datetime "updated_at", precision: 6, null: false
154 | t.string "email", default: "", null: false
155 | t.string "encrypted_password", default: "", null: false
156 | t.string "reset_password_token"
157 | t.datetime "reset_password_sent_at", precision: 6
158 | t.datetime "remember_created_at", precision: 6
159 | t.integer "sign_in_count", default: 0, null: false
160 | t.datetime "current_sign_in_at", precision: 6
161 | t.datetime "last_sign_in_at", precision: 6
162 | t.string "current_sign_in_ip"
163 | t.string "last_sign_in_ip"
164 | t.string "confirmation_token"
165 | t.datetime "confirmed_at", precision: 6
166 | t.datetime "confirmation_sent_at", precision: 6
167 | t.string "unconfirmed_email"
168 | t.integer "failed_attempts", default: 0, null: false
169 | t.string "unlock_token"
170 | t.datetime "locked_at", precision: 6
171 | t.string "provider"
172 | t.string "uid"
173 | t.string "name"
174 | t.string "stripe_customer_id"
175 | t.boolean "is_host", default: false
176 | t.string "stripe_account_id"
177 | t.boolean "charges_enabled", default: false
178 | t.string "phone_number"
179 | t.boolean "identity_verified", default: false
180 | t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
181 | t.index ["email"], name: "index_users_on_email", unique: true
182 | t.index ["provider", "uid"], name: "index_users_on_provider_and_uid"
183 | t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
184 | t.index ["uid"], name: "index_users_on_uid"
185 | t.index ["unlock_token"], name: "index_users_on_unlock_token", unique: true
186 | end
187 |
188 | add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
189 | add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
190 | add_foreign_key "beds", "rooms"
191 | add_foreign_key "calendar_events", "listings"
192 | add_foreign_key "calendar_events", "reservations"
193 | add_foreign_key "listings", "users", column: "host_id"
194 | add_foreign_key "messages", "reservations"
195 | add_foreign_key "messages", "users", column: "from_user_id"
196 | add_foreign_key "messages", "users", column: "to_user_id"
197 | add_foreign_key "photos", "listings"
198 | add_foreign_key "reservations", "listings"
199 | add_foreign_key "reservations", "users", column: "guest_id"
200 | add_foreign_key "rooms", "listings"
201 | end
202 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GIT
2 | remote: https://github.com/rails/rails.git
3 | revision: 4f818c7202ee26b8d33f8817c6064ba94e386596
4 | branch: main
5 | specs:
6 | actioncable (7.1.0.alpha)
7 | actionpack (= 7.1.0.alpha)
8 | activesupport (= 7.1.0.alpha)
9 | nio4r (~> 2.0)
10 | websocket-driver (>= 0.6.1)
11 | zeitwerk (~> 2.6)
12 | actionmailbox (7.1.0.alpha)
13 | actionpack (= 7.1.0.alpha)
14 | activejob (= 7.1.0.alpha)
15 | activerecord (= 7.1.0.alpha)
16 | activestorage (= 7.1.0.alpha)
17 | activesupport (= 7.1.0.alpha)
18 | mail (>= 2.7.1)
19 | net-imap
20 | net-pop
21 | net-smtp
22 | actionmailer (7.1.0.alpha)
23 | actionpack (= 7.1.0.alpha)
24 | actionview (= 7.1.0.alpha)
25 | activejob (= 7.1.0.alpha)
26 | activesupport (= 7.1.0.alpha)
27 | mail (~> 2.5, >= 2.5.4)
28 | net-imap
29 | net-pop
30 | net-smtp
31 | rails-dom-testing (~> 2.0)
32 | actionpack (7.1.0.alpha)
33 | actionview (= 7.1.0.alpha)
34 | activesupport (= 7.1.0.alpha)
35 | rack (~> 2.0, >= 2.2.0)
36 | rack-test (>= 0.6.3)
37 | rails-dom-testing (~> 2.0)
38 | rails-html-sanitizer (~> 1.0, >= 1.2.0)
39 | actiontext (7.1.0.alpha)
40 | actionpack (= 7.1.0.alpha)
41 | activerecord (= 7.1.0.alpha)
42 | activestorage (= 7.1.0.alpha)
43 | activesupport (= 7.1.0.alpha)
44 | globalid (>= 0.6.0)
45 | nokogiri (>= 1.8.5)
46 | actionview (7.1.0.alpha)
47 | activesupport (= 7.1.0.alpha)
48 | builder (~> 3.1)
49 | erubi (~> 1.4)
50 | rails-dom-testing (~> 2.0)
51 | rails-html-sanitizer (~> 1.1, >= 1.2.0)
52 | activejob (7.1.0.alpha)
53 | activesupport (= 7.1.0.alpha)
54 | globalid (>= 0.3.6)
55 | activemodel (7.1.0.alpha)
56 | activesupport (= 7.1.0.alpha)
57 | activerecord (7.1.0.alpha)
58 | activemodel (= 7.1.0.alpha)
59 | activesupport (= 7.1.0.alpha)
60 | activestorage (7.1.0.alpha)
61 | actionpack (= 7.1.0.alpha)
62 | activejob (= 7.1.0.alpha)
63 | activerecord (= 7.1.0.alpha)
64 | activesupport (= 7.1.0.alpha)
65 | marcel (~> 1.0)
66 | mini_mime (>= 1.1.0)
67 | activesupport (7.1.0.alpha)
68 | concurrent-ruby (~> 1.0, >= 1.0.2)
69 | connection_pool (>= 2.2.5)
70 | i18n (>= 1.6, < 2)
71 | minitest (>= 5.1)
72 | tzinfo (~> 2.0)
73 | rails (7.1.0.alpha)
74 | actioncable (= 7.1.0.alpha)
75 | actionmailbox (= 7.1.0.alpha)
76 | actionmailer (= 7.1.0.alpha)
77 | actionpack (= 7.1.0.alpha)
78 | actiontext (= 7.1.0.alpha)
79 | actionview (= 7.1.0.alpha)
80 | activejob (= 7.1.0.alpha)
81 | activemodel (= 7.1.0.alpha)
82 | activerecord (= 7.1.0.alpha)
83 | activestorage (= 7.1.0.alpha)
84 | activesupport (= 7.1.0.alpha)
85 | bundler (>= 1.15.0)
86 | railties (= 7.1.0.alpha)
87 | railties (7.1.0.alpha)
88 | actionpack (= 7.1.0.alpha)
89 | activesupport (= 7.1.0.alpha)
90 | method_source
91 | rake (>= 12.2)
92 | thor (~> 1.0)
93 | zeitwerk (~> 2.6)
94 |
95 | GEM
96 | remote: https://rubygems.org/
97 | specs:
98 | addressable (2.8.0)
99 | public_suffix (>= 2.0.2, < 5.0)
100 | annotate (3.2.0)
101 | activerecord (>= 3.2, < 8.0)
102 | rake (>= 10.4, < 14.0)
103 | bcrypt (3.1.18)
104 | bindex (0.8.1)
105 | bootsnap (1.12.0)
106 | msgpack (~> 1.2)
107 | builder (3.2.4)
108 | byebug (11.1.3)
109 | coderay (1.1.3)
110 | concurrent-ruby (1.1.10)
111 | connection_pool (2.2.5)
112 | crass (1.0.6)
113 | devise (4.8.1)
114 | bcrypt (~> 3.0)
115 | orm_adapter (~> 0.1)
116 | railties (>= 4.1.0)
117 | responders
118 | warden (~> 1.2.3)
119 | digest (3.1.0)
120 | domain_name (0.5.20190701)
121 | unf (>= 0.0.5, < 1.0.0)
122 | erubi (1.10.0)
123 | faker (2.21.0)
124 | i18n (>= 1.8.11, < 2)
125 | faraday (2.3.0)
126 | faraday-net_http (~> 2.0)
127 | ruby2_keywords (>= 0.0.4)
128 | faraday-net_http (2.0.3)
129 | ffi (1.15.5)
130 | ffi-compiler (1.0.1)
131 | ffi (>= 1.0.0)
132 | rake
133 | globalid (1.0.0)
134 | activesupport (>= 5.0)
135 | hashie (5.0.0)
136 | http (5.1.0)
137 | addressable (~> 2.8)
138 | http-cookie (~> 1.0)
139 | http-form_data (~> 2.2)
140 | llhttp-ffi (~> 0.4.0)
141 | http-cookie (1.0.5)
142 | domain_name (~> 0.5)
143 | http-form_data (2.3.0)
144 | i18n (1.12.0)
145 | concurrent-ruby (~> 1.0)
146 | image_processing (1.12.2)
147 | mini_magick (>= 4.9.5, < 5)
148 | ruby-vips (>= 2.0.17, < 3)
149 | jbuilder (2.11.5)
150 | actionview (>= 5.0.0)
151 | activesupport (>= 5.0.0)
152 | jsbundling-rails (1.0.3)
153 | railties (>= 6.0.0)
154 | jwt (2.4.1)
155 | launchy (2.5.0)
156 | addressable (~> 2.7)
157 | letter_opener (1.8.1)
158 | launchy (>= 2.2, < 3)
159 | listen (3.7.1)
160 | rb-fsevent (~> 0.10, >= 0.10.3)
161 | rb-inotify (~> 0.9, >= 0.9.10)
162 | llhttp-ffi (0.4.0)
163 | ffi-compiler (~> 1.0)
164 | rake (~> 13.0)
165 | loofah (2.18.0)
166 | crass (~> 1.0.2)
167 | nokogiri (>= 1.5.9)
168 | mail (2.7.1)
169 | mini_mime (>= 0.1.1)
170 | marcel (1.0.2)
171 | method_source (1.0.0)
172 | mini_magick (4.11.0)
173 | mini_mime (1.1.2)
174 | mini_portile2 (2.8.0)
175 | minitest (5.16.2)
176 | mono_logger (1.1.1)
177 | msgpack (1.5.3)
178 | multi_json (1.15.0)
179 | multi_xml (0.6.0)
180 | mustermann (1.1.1)
181 | ruby2_keywords (~> 0.0.1)
182 | net-imap (0.2.3)
183 | digest
184 | net-protocol
185 | strscan
186 | net-pop (0.1.1)
187 | digest
188 | net-protocol
189 | timeout
190 | net-protocol (0.1.3)
191 | timeout
192 | net-smtp (0.3.1)
193 | digest
194 | net-protocol
195 | timeout
196 | nio4r (2.5.8)
197 | nokogiri (1.13.7)
198 | mini_portile2 (~> 2.8.0)
199 | racc (~> 1.4)
200 | noticed (1.5.9)
201 | http (>= 4.0.0)
202 | rails (>= 5.2.0)
203 | oauth2 (1.4.10)
204 | faraday (>= 0.17.3, < 3.0)
205 | jwt (>= 1.0, < 3.0)
206 | multi_json (~> 1.3)
207 | multi_xml (~> 0.5)
208 | rack (>= 1.2, < 3)
209 | omniauth (2.1.0)
210 | hashie (>= 3.4.6)
211 | rack (>= 2.2.3)
212 | rack-protection
213 | omniauth-google-oauth2 (1.0.1)
214 | jwt (>= 2.0)
215 | oauth2 (~> 1.1)
216 | omniauth (~> 2.0)
217 | omniauth-oauth2 (~> 1.7.1)
218 | omniauth-oauth2 (1.7.3)
219 | oauth2 (>= 1.4, < 3)
220 | omniauth (>= 1.9, < 3)
221 | omniauth-rails_csrf_protection (1.0.1)
222 | actionpack (>= 4.2)
223 | omniauth (~> 2.0)
224 | orm_adapter (0.5.0)
225 | pagy (5.10.1)
226 | activesupport
227 | pg (1.4.1)
228 | pry (0.14.1)
229 | coderay (~> 1.1)
230 | method_source (~> 1.0)
231 | pry-rails (0.3.9)
232 | pry (>= 0.10.4)
233 | public_suffix (4.0.7)
234 | puma (5.6.4)
235 | nio4r (~> 2.0)
236 | racc (1.6.0)
237 | rack (2.2.4)
238 | rack-mini-profiler (2.3.4)
239 | rack (>= 1.2.0)
240 | rack-protection (2.2.0)
241 | rack
242 | rack-test (2.0.2)
243 | rack (>= 1.3)
244 | rails-dom-testing (2.0.3)
245 | activesupport (>= 4.2.0)
246 | nokogiri (>= 1.6)
247 | rails-html-sanitizer (1.4.3)
248 | loofah (~> 2.3)
249 | rake (13.0.6)
250 | rb-fsevent (0.11.1)
251 | rb-inotify (0.10.1)
252 | ffi (~> 1.0)
253 | redis (4.7.1)
254 | redis-namespace (1.8.2)
255 | redis (>= 3.0.4)
256 | responders (3.0.1)
257 | actionpack (>= 5.0)
258 | railties (>= 5.0)
259 | resque (2.2.1)
260 | mono_logger (~> 1.0)
261 | multi_json (~> 1.0)
262 | redis-namespace (~> 1.6)
263 | sinatra (>= 0.9.2)
264 | ruby-vips (2.1.4)
265 | ffi (~> 1.12)
266 | ruby2_keywords (0.0.5)
267 | sass-rails (6.0.0)
268 | sassc-rails (~> 2.1, >= 2.1.1)
269 | sassc (2.4.0)
270 | ffi (~> 1.9)
271 | sassc-rails (2.1.2)
272 | railties (>= 4.0.0)
273 | sassc (>= 2.0)
274 | sprockets (> 3.0)
275 | sprockets-rails
276 | tilt
277 | sinatra (2.2.0)
278 | mustermann (~> 1.0)
279 | rack (~> 2.2)
280 | rack-protection (= 2.2.0)
281 | tilt (~> 2.0)
282 | spring (4.0.0)
283 | sprockets (4.1.1)
284 | concurrent-ruby (~> 1.0)
285 | rack (> 1, < 3)
286 | sprockets-rails (3.4.2)
287 | actionpack (>= 5.2)
288 | activesupport (>= 5.2)
289 | sprockets (>= 3.0.0)
290 | stimulus-rails (1.0.4)
291 | railties (>= 6.0.0)
292 | stripe (6.5.0)
293 | strscan (3.0.3)
294 | tailwindcss-rails (0.4.3)
295 | rails (>= 6.0.0)
296 | thor (1.2.1)
297 | tilt (2.0.10)
298 | timeout (0.3.0)
299 | turbo-rails (1.1.1)
300 | actionpack (>= 6.0.0)
301 | activejob (>= 6.0.0)
302 | railties (>= 6.0.0)
303 | tzinfo (2.0.4)
304 | concurrent-ruby (~> 1.0)
305 | unf (0.1.4)
306 | unf_ext
307 | unf_ext (0.0.8.2)
308 | validates_overlap (1.0.0)
309 | rails (>= 6.0.0)
310 | warden (1.2.9)
311 | rack (>= 2.0.9)
312 | web-console (4.2.0)
313 | actionview (>= 6.0.0)
314 | activemodel (>= 6.0.0)
315 | bindex (>= 0.4.0)
316 | railties (>= 6.0.0)
317 | websocket-driver (0.7.5)
318 | websocket-extensions (>= 0.1.0)
319 | websocket-extensions (0.1.5)
320 | zeitwerk (2.6.0)
321 |
322 | PLATFORMS
323 | ruby
324 |
325 | DEPENDENCIES
326 | annotate
327 | bootsnap
328 | byebug
329 | devise
330 | faker
331 | image_processing (~> 1.2)
332 | jbuilder (~> 2.7)
333 | jsbundling-rails
334 | letter_opener
335 | listen (~> 3.3)
336 | noticed (~> 1.5)
337 | omniauth-google-oauth2
338 | omniauth-rails_csrf_protection
339 | pagy (~> 5.10)
340 | pg (~> 1.1)
341 | pry-rails (~> 0.3.9)
342 | puma (~> 5.0)
343 | rack-mini-profiler (~> 2.0)
344 | rails!
345 | resque
346 | sass-rails (>= 6)
347 | spring
348 | sprockets-rails
349 | stimulus-rails
350 | stripe
351 | tailwindcss-rails (~> 0.4.3)
352 | turbo-rails
353 | tzinfo-data
354 | validates_overlap (~> 1.0)
355 | web-console (>= 4.1.0)
356 |
357 | RUBY VERSION
358 | ruby 3.1.2p20
359 |
360 | BUNDLED WITH
361 | 2.3.17
362 |
--------------------------------------------------------------------------------
/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Clearbnb
5 |
6 | <%= csrf_meta_tags %>
7 | <%= csp_meta_tag %>
8 |
9 | <%= stylesheet_link_tag "inter-font", "data-turbo-track": "reload" %>
10 | <%= stylesheet_link_tag "tailwind", "data-turbo-track": "reload" %>
11 | <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
12 | <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | <% navigation_items.each do |item| %>
31 | <%= link_to(item[:body], item[:href], class: navigation_class(item[:href]), 'aria-current': navigation_aria(item[:href])) %>
32 | <% end %>
33 |
34 |
35 |
36 |
37 |
38 |
39 | View notifications
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
54 |
55 |
56 |
66 |
74 |
75 |
76 | <% if user_signed_in? %>
77 |
78 |
79 |
80 | <% else %>
81 |
82 | <% end %>
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 | Open main menu
92 |
97 |
98 |
99 |
100 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 | <%= yield %>
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
--------------------------------------------------------------------------------