bookingsync_portal 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +174 -0
  4. data/Rakefile +28 -0
  5. data/app/assets/config/manifest.js +3 -0
  6. data/app/assets/images/bookingsync_portal/bookingsync.png +0 -0
  7. data/app/assets/images/bookingsync_portal/help/connect.gif +0 -0
  8. data/app/assets/javascripts/bookingsync_portal/admin/application.js.coffee +83 -0
  9. data/app/assets/javascripts/bookingsync_portal/admin/connections.js.coffee +3 -0
  10. data/app/assets/javascripts/bookingsync_portal/admin/handle_remote_errors.js.coffee +3 -0
  11. data/app/assets/javascripts/bookingsync_portal/admin/lib/list-filter.js.coffee +28 -0
  12. data/app/assets/javascripts/bookingsync_portal/admin/live_updates.js.coffee +7 -0
  13. data/app/assets/javascripts/bookingsync_portal/admin/rentals.js.coffee +12 -0
  14. data/app/assets/javascripts/bookingsync_portal/admin/templates/filter_input.hbs +6 -0
  15. data/app/assets/javascripts/bookingsync_portal/admin/vendor/css-contains.js +29 -0
  16. data/app/assets/stylesheets/bookingsync_portal/admin/application.scss +241 -0
  17. data/app/controllers/bookingsync_portal/admin/base_controller.rb +25 -0
  18. data/app/controllers/bookingsync_portal/admin/connections_controller.rb +48 -0
  19. data/app/controllers/bookingsync_portal/admin/help_controller.rb +7 -0
  20. data/app/controllers/bookingsync_portal/admin/remote_accounts_controller.rb +27 -0
  21. data/app/controllers/bookingsync_portal/admin/rentals_controller.rb +36 -0
  22. data/app/controllers/bookingsync_portal/application_controller.rb +4 -0
  23. data/app/helpers/bookingsync_portal/admin/application_helper.rb +34 -0
  24. data/app/models/bookingsync_portal/account.rb +15 -0
  25. data/app/models/bookingsync_portal/connection.rb +29 -0
  26. data/app/models/bookingsync_portal/photo.rb +8 -0
  27. data/app/models/bookingsync_portal/rate.rb +6 -0
  28. data/app/models/bookingsync_portal/remote_account.rb +9 -0
  29. data/app/models/bookingsync_portal/remote_rental.rb +31 -0
  30. data/app/models/bookingsync_portal/rental.rb +26 -0
  31. data/app/synchronizers/bookingsync_portal/write/ensure_source_exists.rb +22 -0
  32. data/app/synchronizers/bookingsync_portal/write/source.rb +30 -0
  33. data/app/views/bookingsync_portal/admin/connections/create.js.coffee +6 -0
  34. data/app/views/bookingsync_portal/admin/connections/destroy.js.coffee +7 -0
  35. data/app/views/bookingsync_portal/admin/help/_help.html.erb +5 -0
  36. data/app/views/bookingsync_portal/admin/help/index.html.erb +7 -0
  37. data/app/views/bookingsync_portal/admin/remote_accounts/_form.html.erb +11 -0
  38. data/app/views/bookingsync_portal/admin/remote_accounts/_how_to_connect.html.erb +1 -0
  39. data/app/views/bookingsync_portal/admin/remote_accounts/new.html.erb +5 -0
  40. data/app/views/bookingsync_portal/admin/rentals/_connected_rental.html.erb +30 -0
  41. data/app/views/bookingsync_portal/admin/rentals/_new_remote_account.html.erb +3 -0
  42. data/app/views/bookingsync_portal/admin/rentals/_new_remote_rental.html.erb +6 -0
  43. data/app/views/bookingsync_portal/admin/rentals/_remote_rental.html.erb +11 -0
  44. data/app/views/bookingsync_portal/admin/rentals/_rental.html.erb +14 -0
  45. data/app/views/bookingsync_portal/admin/rentals/_rentals.html.erb +14 -0
  46. data/app/views/bookingsync_portal/admin/rentals/index.html.erb +56 -0
  47. data/app/views/bookingsync_portal/admin/rentals/show.js.erb +7 -0
  48. data/app/views/layouts/bookingsync_portal/_flash.html.erb +8 -0
  49. data/app/views/layouts/bookingsync_portal/_menu.html.erb +30 -0
  50. data/app/views/layouts/bookingsync_portal/admin.html.erb +21 -0
  51. data/app/views/layouts/bookingsync_portal/fullscreen/application.html.erb +19 -0
  52. data/config/locales/en.yml +85 -0
  53. data/config/routes.rb +9 -0
  54. data/db/migrate/20150222172825_create_accounts.rb +17 -0
  55. data/db/migrate/20150222173413_create_rentals.rb +14 -0
  56. data/db/migrate/20150222173711_create_remote_accounts.rb +10 -0
  57. data/db/migrate/20150222174023_create_remote_rentals.rb +12 -0
  58. data/db/migrate/20150222174234_create_connections.rb +10 -0
  59. data/db/migrate/20150520134912_create_photos.rb +13 -0
  60. data/db/migrate/20150521091056_create_rates.rb +12 -0
  61. data/db/migrate/20151210164752_add_synced_source_id_to_account.rb +5 -0
  62. data/db/migrate/20160301141356_create_bookings.rb +9 -0
  63. data/lib/bookingsync_portal.rb +77 -0
  64. data/lib/bookingsync_portal/booking_map.rb +109 -0
  65. data/lib/bookingsync_portal/engine.rb +18 -0
  66. data/lib/bookingsync_portal/mash_serializer.rb +9 -0
  67. data/lib/bookingsync_portal/version.rb +3 -0
  68. data/lib/generators/bookingsync_portal/install_generator.rb +22 -0
  69. data/lib/generators/templates/initializers/bookingsync_portal.rb +48 -0
  70. data/lib/generators/templates/models/account.rb +14 -0
  71. data/lib/generators/templates/models/connection.rb +2 -0
  72. data/lib/generators/templates/models/photo.rb +4 -0
  73. data/lib/generators/templates/models/rate.rb +3 -0
  74. data/lib/generators/templates/models/remote_account.rb +6 -0
  75. data/lib/generators/templates/models/remote_rental.rb +6 -0
  76. data/lib/generators/templates/models/rental.rb +11 -0
  77. data/lib/tasks/bookingsync_portal_tasks.rake +4 -0
  78. metadata +521 -0
@@ -0,0 +1,25 @@
1
+ require 'bookingsync_application/controllers/common_base'
2
+
3
+ module BookingsyncPortal
4
+ module Admin
5
+ class BaseController < ApplicationController
6
+ helper BookingsyncPortal::Admin::ApplicationHelper
7
+ layout 'bookingsync_portal/admin'
8
+ respond_to :html
9
+ include BookingsyncApplication::Controllers::CommonBase
10
+
11
+ before_action :enforce_remote_account!
12
+
13
+ private
14
+
15
+ def enforce_remote_account!
16
+ redirect_to new_admin_remote_account_path if current_account.remote_accounts.empty?
17
+ end
18
+
19
+ def messagebus_channel
20
+ "/account-#{current_account.id}"
21
+ end
22
+ helper_method :messagebus_channel
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,48 @@
1
+ module BookingsyncPortal
2
+ module Admin
3
+ class ConnectionsController < Admin::BaseController
4
+ def create
5
+ if remote_account? && BookingsyncPortal.create_remote_rental
6
+ new_remote_rental = BookingsyncPortal.remote_rental_model.constantize.new(remote_account: remote_account)
7
+ @connection = rental.create_connection(remote_rental: new_remote_rental)
8
+ else
9
+ @connection = rental.create_connection(remote_rental: remote_rental)
10
+ end
11
+
12
+ respond_to do |wants|
13
+ wants.html { redirect_to admin_rentals_path }
14
+ wants.js
15
+ end
16
+ end
17
+
18
+ def destroy
19
+ @connection = current_account.connections.find(params[:id]).destroy
20
+ @not_connected_rentals = current_account.rentals.visible.ordered.not_connected
21
+ @visible_rentals = current_account.rentals.visible
22
+
23
+ respond_to do |wants|
24
+ wants.html { redirect_to admin_rentals_path }
25
+ wants.js
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def rental
32
+ @rental ||= current_account.rentals.not_connected.visible.find(params[:rental_id])
33
+ end
34
+
35
+ def remote_rental
36
+ @remote_rental ||= current_account.remote_rentals.not_connected.find(params[:remote_rental_id])
37
+ end
38
+
39
+ def remote_account
40
+ @remote_account ||= current_account.remote_accounts.find(params[:remote_account_id])
41
+ end
42
+
43
+ def remote_account?
44
+ params[:remote_account_id].present? && remote_account
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,7 @@
1
+ module BookingsyncPortal
2
+ module Admin
3
+ class HelpController < Admin::BaseController
4
+ layout "bookingsync_portal/fullscreen/application"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,27 @@
1
+ module BookingsyncPortal
2
+ module Admin
3
+ class RemoteAccountsController < Admin::BaseController
4
+ skip_before_action :enforce_remote_account!
5
+
6
+ def new
7
+ @remote_account = scope.build
8
+ end
9
+
10
+ def create
11
+ BookingsyncPortal::Write::EnsureSourceExists.new(current_account).call
12
+ @remote_account = scope.create(params_remote_account)
13
+ respond_with @remote_account, location: admin_rentals_path
14
+ end
15
+
16
+ private
17
+
18
+ def scope
19
+ current_account.remote_accounts
20
+ end
21
+
22
+ def params_remote_account
23
+ params.require(:remote_account).permit(:uid)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,36 @@
1
+ module BookingsyncPortal
2
+ module Admin
3
+ class RentalsController < Admin::BaseController
4
+ before_action :synchronize_rentals, only: :index
5
+ before_action :fetch_remote_rentals, only: :index
6
+
7
+ def index
8
+ @not_connected_rentals = current_account.rentals.visible.ordered.not_connected
9
+ @visible_rentals = current_account.rentals.visible
10
+ @remote_accounts = current_account.remote_accounts
11
+ @remote_rentals_by_account = current_account.remote_rentals.ordered
12
+ .includes(:remote_account, :rental).group_by(&:remote_account)
13
+ end
14
+
15
+ def show
16
+ rental
17
+ end
18
+
19
+ private
20
+
21
+ def synchronize_rentals
22
+ BookingsyncPortal.rental_model.constantize.synchronize(scope: current_account)
23
+ end
24
+
25
+ def fetch_remote_rentals
26
+ unless BookingsyncPortal.fetch_remote_rentals(current_account)
27
+ @remote_account_not_registered = true
28
+ end
29
+ end
30
+
31
+ def rental
32
+ @rental ||= current_account.rentals.visible.find(params[:id])
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,4 @@
1
+ module BookingsyncPortal
2
+ class ApplicationController < ::ApplicationController
3
+ end
4
+ end
@@ -0,0 +1,34 @@
1
+ module BookingsyncPortal
2
+ module Admin
3
+ module ApplicationHelper
4
+ def rental_details(rental)
5
+ scope = 'bookingsync_portal.admin.rentals.rental'
6
+
7
+ details = Array.new
8
+ if rental.sleeps.to_i > 0
9
+ if rental.sleeps_max.to_i > 0 && rental.sleeps_max.to_i > rental.sleeps.to_i
10
+ details << t(:sleeps_html, scope: scope,
11
+ count: "#{rental.sleeps.to_i}-#{rental.sleeps_max.to_i}")
12
+ else
13
+ details << t(:sleeps_html, scope: scope,
14
+ count: rental.sleeps.to_i)
15
+ end
16
+ end
17
+ if rental.bedrooms_count.to_i > 0
18
+ details << t(:bedrooms_html, scope: scope,
19
+ count: rental.bedrooms_count.to_i)
20
+ end
21
+ if rental.bathrooms_count.to_i > 0
22
+ details << t(:bathrooms_html, scope: scope,
23
+ count: rental.bathrooms_count.to_i)
24
+ end
25
+ if rental.surface.to_i > 0
26
+ details << t(:surface_html, scope: scope,
27
+ count: rental.surface.to_i, unit: rental.surface_unit_symbol)
28
+ end
29
+
30
+ safe_join(details, ', ')
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,15 @@
1
+ class BookingsyncPortal::Account < ActiveRecord::Base
2
+ self.table_name = 'accounts'
3
+
4
+ include BookingSync::Engine::Models::Account
5
+
6
+ has_many :remote_accounts, class_name: BookingsyncPortal.remote_account_model, dependent: :destroy
7
+ has_many :remote_rentals, class_name: BookingsyncPortal.remote_rental_model, through: :remote_accounts
8
+ has_many :rentals, class_name: BookingsyncPortal.rental_model, dependent: :destroy
9
+ has_many :connections, class_name: BookingsyncPortal.connection_model, through: :rentals
10
+
11
+ # Used to synchronize accounts using `Account.synchronize(remove: true)`
12
+ def self.api
13
+ BookingSync::API::Client.new(BookingSync::Engine.application_token.token)
14
+ end
15
+ end
@@ -0,0 +1,29 @@
1
+ class BookingsyncPortal::Connection < ActiveRecord::Base
2
+ self.table_name = 'connections'
3
+
4
+ belongs_to :remote_rental, class_name: BookingsyncPortal.remote_rental_model
5
+ belongs_to :rental, class_name: BookingsyncPortal.rental_model
6
+
7
+ validates :remote_rental, presence: true
8
+ validates :rental, presence: true
9
+
10
+ validate :matching_accounts, if: -> { rental && remote_rental }
11
+
12
+ after_commit :notify_via_message_bus
13
+
14
+ def notify_via_message_bus
15
+ MessageBus.publish "/account-#{rental.account_id}", { refresh_from:
16
+ BookingsyncPortal::Engine.routes.url_helpers.admin_rental_path(rental, format: :js)
17
+ }
18
+ end
19
+
20
+ private
21
+
22
+ def matching_accounts?
23
+ rental.account == remote_rental.remote_account.account
24
+ end
25
+
26
+ def matching_accounts
27
+ errors.add(:base, :not_matching_accounts) unless matching_accounts?
28
+ end
29
+ end
@@ -0,0 +1,8 @@
1
+ class BookingsyncPortal::Photo < ActiveRecord::Base
2
+ self.table_name = 'photos'
3
+
4
+ belongs_to :rental
5
+ has_one :account, through: :rental
6
+
7
+ scope :ordered, -> { order position: :asc }
8
+ end
@@ -0,0 +1,6 @@
1
+ class BookingsyncPortal::Rate < ActiveRecord::Base
2
+ self.table_name = 'rates'
3
+
4
+ belongs_to :rental
5
+ has_one :account, through: :rental
6
+ end
@@ -0,0 +1,9 @@
1
+ class BookingsyncPortal::RemoteAccount < ActiveRecord::Base
2
+ self.table_name = 'remote_accounts'
3
+
4
+ belongs_to :account, class_name: BookingsyncPortal.account_model
5
+ has_many :remote_rentals, class_name: BookingsyncPortal.remote_rental_model, dependent: :destroy
6
+
7
+ validates :uid, presence: true, uniqueness: true
8
+ validates :account, presence: true
9
+ end
@@ -0,0 +1,31 @@
1
+ require 'bookingsync_portal/mash_serializer'
2
+
3
+ class BookingsyncPortal::RemoteRental < ActiveRecord::Base
4
+ self.table_name = 'remote_rentals'
5
+
6
+ belongs_to :remote_account, class_name: BookingsyncPortal.remote_account_model
7
+ has_one :account, class_name: BookingsyncPortal.account_model, through: :remote_account
8
+ has_one :connection, class_name: BookingsyncPortal.connection_model, dependent: :destroy
9
+ has_one :rental, class_name: BookingsyncPortal.rental_model, through: :connection
10
+
11
+ serialize :remote_data, BookingsyncPortal::MashSerializer
12
+
13
+ validates :uid, uniqueness: { allow_nil: true }
14
+ validates :remote_account, presence: true
15
+
16
+ scope :ordered, -> { order(created_at: :desc) }
17
+ scope :connected, -> { joins(:rental) }
18
+ scope :not_connected, -> { includes(:rental).where(rentals: { id: nil }) }
19
+
20
+ def display_name
21
+ uid
22
+ end
23
+
24
+ def connected?
25
+ rental.present?
26
+ end
27
+
28
+ def synchronized?
29
+ synchronized_at.present?
30
+ end
31
+ end
@@ -0,0 +1,26 @@
1
+ class BookingsyncPortal::Rental < ActiveRecord::Base
2
+ self.table_name = 'rentals'
3
+
4
+ belongs_to :account, class_name: BookingsyncPortal.account_model
5
+ has_one :connection, class_name: BookingsyncPortal.connection_model, dependent: :destroy
6
+ has_one :remote_rental, class_name: BookingsyncPortal.remote_rental_model, through: :connection
7
+ has_many :photos, class_name: BookingsyncPortal.photo_model, dependent: :destroy
8
+ if BookingsyncPortal.rate_model.present?
9
+ has_many :rates, class_name: BookingsyncPortal.rate_model, dependent: :destroy
10
+ end
11
+
12
+ validates :synced_id, uniqueness: true, presence: true
13
+
14
+ scope :ordered, -> { order(position: :asc) }
15
+ scope :connected, -> { joins(:remote_rental) }
16
+ scope :not_connected, -> { includes(:connection).where(connections: { remote_rental_id: nil }) }
17
+ scope :visible, -> { all }
18
+
19
+ def connected?
20
+ remote_rental.present?
21
+ end
22
+
23
+ def ordered_photos
24
+ photos.ordered
25
+ end
26
+ end
@@ -0,0 +1,22 @@
1
+ class BookingsyncPortal::Write::EnsureSourceExists
2
+ attr_reader :account
3
+
4
+ def initialize(account)
5
+ @account = account
6
+ end
7
+
8
+ def call
9
+ blank_source? ? update_source : true
10
+ end
11
+
12
+ private
13
+
14
+ def blank_source?
15
+ account.synced_source_id.blank?
16
+ end
17
+
18
+ def update_source
19
+ source = BookingsyncPortal::Write::Source.new(account).synchronize
20
+ account.update!(synced_source_id: source.id)
21
+ end
22
+ end
@@ -0,0 +1,30 @@
1
+ class BookingsyncPortal::Write::Source
2
+ attr_reader :account
3
+ private :account
4
+
5
+ def initialize(account)
6
+ @account = account
7
+ end
8
+
9
+ def synchronize
10
+ find_or_create
11
+ end
12
+
13
+ private
14
+
15
+ def find_or_create
16
+ find_source || create_source
17
+ end
18
+
19
+ def api
20
+ account.api
21
+ end
22
+
23
+ def find_source
24
+ api.sources.find { |source| source.name.downcase == BookingsyncPortal.source_name.downcase }
25
+ end
26
+
27
+ def create_source
28
+ api.create_source(name: BookingsyncPortal.source_name)
29
+ end
30
+ end
@@ -0,0 +1,6 @@
1
+ <%- if @connection.persisted? %>
2
+ $("#<%= dom_id @connection.remote_rental %>, .new_rental_placeholder").first().replaceWith(
3
+ "<%=j render 'bookingsync_portal/admin/rentals/connected_rental', rental: @connection.rental, remote_rental: @connection.remote_rental %>");
4
+ $(".bookingsync-rental").draggableRental()
5
+ $(".panel.panel-remote").droppableRemoteRental()
6
+ <%- end %>
@@ -0,0 +1,7 @@
1
+ $("#<%= dom_id @connection.rental %>").first().replaceWith(
2
+ "<%=j render 'bookingsync_portal/admin/rentals/remote_rental', remote_rental: @connection.remote_rental %>")
3
+ $(".bookingsync-rentals-list .rentals-list-scroll").first().html(
4
+ "<%=j render 'bookingsync_portal/admin/rentals/rentals', visible_rentals: @visible_rentals, not_connected_rentals: @not_connected_rentals %>");
5
+
6
+ $(".bookingsync-rental").draggableRental()
7
+ $(".panel.panel-remote").droppableRemoteRental()
@@ -0,0 +1,5 @@
1
+ <h2><%=t '.publish_rentals.title' %></h2>
2
+ <p><%=t '.publish_rentals.description_html' %></p>
3
+ <h2><%=t '.connect.title' %></h2>
4
+ <p><%=t '.connect.description_html' %></p>
5
+ <%= image_tag 'bookingsync_portal/help/connect.gif', class: "img-responsive" %>
@@ -0,0 +1,7 @@
1
+ <article>
2
+ <header>
3
+ <%= link_to icon("times", t('.close')), admin_root_path, class: "pull-right" %>
4
+ <h1><%=t '.title' %></h1>
5
+ </header>
6
+ <%= render 'help' %>
7
+ </article>
@@ -0,0 +1,11 @@
1
+ <div class="col-sm-offset-4 col-sm-4">
2
+ <%= simple_form_for [:admin, remote_account] do |f| -%>
3
+ <%= f.input :uid, wrapper_html: { class: 'form-group' },
4
+ label_html: { class: 'label-lg' }, input_html: { class: 'input-lg form-control' } -%>
5
+
6
+ <div class="form-group">
7
+ <%= f.submit t('.submit_new', portal_name: BookingsyncPortal.portal_name),
8
+ class: "btn btn-primary btn-lg btn-block" %>
9
+ </div>
10
+ <%- end -%>
11
+ </div>
@@ -0,0 +1 @@
1
+ <p class="lead">Info on how to connect with remote account, modify app/views/bookingsync_portal/admin/remote_accounts/_how_to_connect.html.erb</p>
@@ -0,0 +1,5 @@
1
+ <div class="text-center">
2
+ <h1><%=t '.title', portal_name: BookingsyncPortal.portal_name %></h1>
3
+ <%= render 'how_to_connect' %>
4
+ <%= render 'form', remote_account: @remote_account %>
5
+ </div>
@@ -0,0 +1,30 @@
1
+ <div id="<%= dom_id rental %>"
2
+ class="panel panel-connected <%= "pending" unless rental.remote_rental.synchronized? %>">
3
+
4
+ <div class="panel-body-grid">
5
+ <div class="panel-body-grid-photo">
6
+ <%- if rental.ordered_photos.first -%>
7
+ <%= image_tag rental.ordered_photos.first.thumb_url, class: "img-responsive" %>
8
+ <%- end -%>
9
+ </div>
10
+ <div class="panel-body-grid-text">
11
+ <h4 title="<%= RemoteRental.human_attribute_name(:uid) %>: <%= remote_rental.uid %>">
12
+ <%= rental.name %><br>
13
+ <small><%= remote_rental.display_name %></small>
14
+ </h4>
15
+ <div class="status">
16
+ <%= link_to BookingsyncPortal::Engine.routes.url_helpers.admin_connection_path(rental.connection),
17
+ class: ["btn", "btn-xs", "remove-connection", rental.remote_rental.synchronized? ? "btn-success" : "btn-warning"],
18
+ data: { disable_with: t('.disconnecting_rental') }, method: :delete, remote: true do %>
19
+ <% unless rental.remote_rental.synchronized? %>
20
+ <span class="hover_hide"><%= icon('spinner') %></span>
21
+ <% else %>
22
+ <span class="hover_hide"><%= icon('check') %></span>
23
+ <% end %>
24
+ <span class="hover_show"><%= t('.disconnect_rental') %></span>
25
+ <% end %>
26
+ </div>
27
+ <p class="text-overflow"><%= rental_details(rental) %></p>
28
+ </div>
29
+ </div>
30
+ </div>