stay_commerce 0.1.11 → 0.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/stay/admin/property.js +8 -8
  3. data/app/controllers/concerns/stay/image_resizer_concern.rb +44 -0
  4. data/app/controllers/devise/api/tokens_controller.rb +0 -4
  5. data/app/controllers/stay/admin/properties_controller.rb +1 -0
  6. data/app/controllers/stay/admin/rooms_controller.rb +2 -2
  7. data/app/controllers/stay/api/v1/amenities_controller.rb +38 -6
  8. data/app/controllers/stay/api/v1/features_controller.rb +32 -6
  9. data/app/controllers/stay/api/v1/properties_controller.rb +1 -0
  10. data/app/controllers/stay/api/v1/property_categories_controller.rb +43 -5
  11. data/app/controllers/stay/api/v1/property_types_controller.rb +32 -9
  12. data/app/controllers/stay/users/registrations_controller.rb +7 -0
  13. data/app/controllers/stay/users/sessions_controller.rb +7 -0
  14. data/app/helpers/stay/application_helper.rb +44 -14
  15. data/app/models/stay/property.rb +4 -3
  16. data/app/models/stay/property_category.rb +2 -1
  17. data/app/models/stay/property_type.rb +3 -3
  18. data/app/models/stay/room_type.rb +3 -2
  19. data/app/serializers/credit_card_serializer.rb +0 -3
  20. data/app/serializers/feature_serializer.rb +3 -0
  21. data/app/serializers/property_category_serializer.rb +1 -5
  22. data/app/serializers/property_listing_serializer.rb +1 -1
  23. data/app/serializers/property_serializer.rb +2 -2
  24. data/app/serializers/property_type_serializer.rb +1 -5
  25. data/app/serializers/room_serializer.rb +1 -1
  26. data/app/serializers/user_serializer.rb +2 -9
  27. data/app/views/stay/admin/properties/index.html.erb +60 -60
  28. data/app/views/stay/admin/rooms/index.html.erb +51 -52
  29. data/app/views/stay/admin/users/show.html.erb +6 -3
  30. data/db/migrate/20250407101335_add_field_to_stay_users.rb +7 -0
  31. data/lib/stay/version.rb +1 -1
  32. metadata +5 -3
  33. data/app/serializers/property_feature_serializer.rb +0 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a5861ce1241022b986eecc6189dbb408fc598a09d8918711c0c9610f917d24a6
4
- data.tar.gz: 061efeee0f6282c67ac3dcaf4e578f25032dcee4a173b46ccccf1073b09e7116
3
+ metadata.gz: e8ab666ef6621d55e7d2aac249f86d0ea9e2c0ccb902bda7de026a5013181372
4
+ data.tar.gz: 432b2ded3073a63f4757e7721dec5c9e21203f3a51377382f23eb5a503650d52
5
5
  SHA512:
6
- metadata.gz: 745c862563d5b73069e9e17b9f4a98a6c67bc3f3034dcf5c166ee425a79fd16fc95806d80752f212b92fa8d368bff27224b5ed8a37792f4f542fae3c94e2c1db
7
- data.tar.gz: c508c74362a75aa3a0d2dbc5f5f01e6172dc3df0928fc10ebf8112a2565a80d2ba01ec8b1aa660890e5cb0f42ada171dc12b7e8072796cc9bff7671dddbb3215
6
+ metadata.gz: d046d3c8f75dc3e92614d66d991770d3d03a606458aec4c81fc3a3a9108fc1d2c5f8e45aea562cc8d62b50d55b8f9325949a16451daf41683ef59e33f20a0014
7
+ data.tar.gz: aa34ce806d5e6f5300f7386f359dcc57d25e1920a869b2181e327710480cd88007846930e378dc2e77256e2b528dd784ddbf1c5b46a545c368c92c8b3853e66c
@@ -25,13 +25,13 @@ $(document).ready(function() {
25
25
  const roomTypeField = document.getElementById('room_type_field');
26
26
  const propertyCategoryData = document.getElementById('property-category-data');
27
27
 
28
- // const roomTypesByCategory = JSON.parse(propertyCategoryData.getAttribute('data-room-types'));
28
+ const roomTypesByCategory = JSON.parse(propertyCategoryData.getAttribute('data-room-types'));
29
29
 
30
- // window.updateRoomType = function(selectedCategoryId) {
31
- // if (roomTypesByCategory[selectedCategoryId]) {
32
- // roomTypeField.value = roomTypesByCategory[selectedCategoryId];
33
- // } else {
34
- // roomTypeField.value = '';
35
- // }
36
- // };
30
+ window.updateRoomType = function(selectedCategoryId) {
31
+ if (roomTypesByCategory[selectedCategoryId]) {
32
+ roomTypeField.value = roomTypesByCategory[selectedCategoryId];
33
+ } else {
34
+ roomTypeField.value = '';
35
+ }
36
+ };
37
37
  });
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+ module Stay
3
+ module ImageResizerConcern
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ before_action :resize_image, only: %i[update create]
8
+ end
9
+
10
+ private
11
+
12
+ def resize_image
13
+ image_params = resize_param_name
14
+ return unless image_params
15
+
16
+ Array(image_params).each do |image_param|
17
+ next unless image_param.respond_to?(:tempfile)
18
+
19
+ content_type = Marcel::MimeType.for(image_param.tempfile)
20
+
21
+ next unless content_type&.start_with?("image")
22
+
23
+ begin
24
+ ImageProcessing::MiniMagick
25
+ .source(image_param.tempfile)
26
+ .quality(85)
27
+ .call(destination: image_param.tempfile.path)
28
+ rescue StandardError => e
29
+ Rails.logger.error "Image processing failed: #{e.message}"
30
+ next
31
+ end
32
+ end
33
+ end
34
+
35
+ def resize_param_name
36
+ case controller_name
37
+ when "properties"
38
+ params[:property][:place_images] || params[:property][:cover_image]
39
+ else
40
+ params[:attachment]
41
+ end
42
+ end
43
+ end
44
+ end
@@ -27,12 +27,10 @@ module Devise
27
27
  token = service.success
28
28
 
29
29
  call_devise_trackable!(token.resource_owner)
30
-
31
30
  token_response = Devise::Api::Responses::TokenResponse.new(request, token: token, action: __method__)
32
31
  Devise.api.config.after_successful_sign_up.call(token.resource_owner, token, request)
33
32
  assign_role(token.resource_owner) if token.resource_owner
34
33
  data = ActiveModelSerializers::SerializableResource.new(token.resource_owner, serializer: UserSerializer).as_json
35
- UserMailer.welcome_email(token.resource_owner).deliver_later if token.resource_owner
36
34
  return render json: { access_token: token_response&.token.access_token, success: true }, status: token_response.status
37
35
  end
38
36
 
@@ -157,8 +155,6 @@ module Devise
157
155
  def assign_role(user)
158
156
  role = Stay::Role.find_by(name: params[:type])
159
157
  user.role_users.create(role_id: role&.id) if role.present?
160
- token = Random.hex(16)
161
- user.update(confirmation_token: token)
162
158
  end
163
159
 
164
160
  def sign_in_params
@@ -1,6 +1,7 @@
1
1
  module Stay
2
2
  module Admin
3
3
  class PropertiesController < Stay::Admin::BaseController
4
+ include Stay::ImageResizerConcern
4
5
  before_action :set_property, only: %i[show edit update destroy approve reject]
5
6
 
6
7
  def index
@@ -5,7 +5,7 @@ module Stay
5
5
  before_action :set_room, only: %i[show edit update destroy]
6
6
 
7
7
  def index
8
- @rooms = @property.rooms
8
+ @rooms = @property.rooms.page(params[:page]).per(10)
9
9
  end
10
10
 
11
11
  def show
@@ -51,7 +51,7 @@ module Stay
51
51
  end
52
52
 
53
53
  def room_params
54
- params.require(:room).permit(:property_id, :max_guests, :price_per_month, :room_type_id, :booking_start, :booking_end, :description,
54
+ params.require(:room).permit(:property_id, :max_guests, :price_per_month, :room_type_id, :booking_start, :booking_end, :description, :name,
55
55
  :size, :bed_type_id, :status, amenity_ids: [], feature_ids: [], room_images: [],
56
56
  room_amenities_attributes: [ :id, :amenity_id, :_destroy ],
57
57
  room_features_attributes: [ :id, :name, :feature_id, :_destroy ],
@@ -1,13 +1,45 @@
1
1
  class Stay::Api::V1::AmenitiesController < ApplicationController
2
- before_action :authenticate_devise_api_token!
3
-
4
2
  def property
5
- amenities = Stay::Amenity.where(amenity_type: "property").all
6
- render json: { data: ActiveModelSerializers::SerializableResource.new(amenities, each_serializer: AmenitySerializer) }
3
+ amenities = Stay::Amenity.property
4
+
5
+ if amenities.exists?
6
+ render json: {
7
+ success: true,
8
+ data: ActiveModelSerializers::SerializableResource.new(amenities, each_serializer: AmenitySerializer)
9
+ }, status: :ok
10
+ else
11
+ render json: {
12
+ success: false,
13
+ message: "No property amenities found"
14
+ }, status: :not_found
15
+ end
16
+ rescue => e
17
+ render json: {
18
+ success: false,
19
+ error: "Failed to fetch property amenities",
20
+ message: e.message
21
+ }, status: :internal_server_error
7
22
  end
8
23
 
9
24
  def room
10
- amenities = Stay::Amenity.where(amenity_type: "room").all
11
- render json: { data: ActiveModelSerializers::SerializableResource.new(amenities, each_serializer: AmenitySerializer) }
25
+ amenities = Stay::Amenity.room
26
+
27
+ if amenities.exists?
28
+ render json: {
29
+ success: true,
30
+ data: ActiveModelSerializers::SerializableResource.new(amenities, each_serializer: AmenitySerializer)
31
+ }, status: :ok
32
+ else
33
+ render json: {
34
+ success: false,
35
+ message: "No room amenities found"
36
+ }, status: :not_found
37
+ end
38
+ rescue => e
39
+ render json: {
40
+ success: false,
41
+ error: "Failed to fetch room amenities",
42
+ message: e.message
43
+ }, status: :internal_server_error
12
44
  end
13
45
  end
@@ -3,19 +3,45 @@ class Stay::Api::V1::FeaturesController < Stay::BaseApiController
3
3
 
4
4
  def property
5
5
  features = Stay::Feature.property
6
- if features.any?
7
- render json: { data:ActiveModelSerializers::SerializableResource.new(features, each_serializer: PropertyFeatureSerializer) ,success: true }, status: :ok
6
+
7
+ if features.exists?
8
+ render json: {
9
+ success: true,
10
+ data: ActiveModelSerializers::SerializableResource.new(features, each_serializer: FeatureSerializer)
11
+ }, status: :ok
8
12
  else
9
- render json: { error:"no feature found", success: false }, status: :unprocessable_entity
13
+ render json: {
14
+ success: false,
15
+ message: "No property features found"
16
+ }, status: :not_found
10
17
  end
18
+ rescue => e
19
+ render json: {
20
+ success: false,
21
+ error: "Failed to fetch property features",
22
+ message: e.message
23
+ }, status: :internal_server_error
11
24
  end
12
25
 
13
26
  def room
14
27
  features = Stay::Feature.room
15
- if features.any?
16
- render json: { data:ActiveModelSerializers::SerializableResource.new(features, each_serializer: PropertyFeatureSerializer) ,success: true }, status: :ok
28
+
29
+ if features.exists?
30
+ render json: {
31
+ success: true,
32
+ data: ActiveModelSerializers::SerializableResource.new(features, each_serializer: FeatureSerializer)
33
+ }, status: :ok
17
34
  else
18
- render json: { error:"no feature found", success: false }, status: :unprocessable_entity
35
+ render json: {
36
+ success: false,
37
+ message: "No room features found"
38
+ }, status: :not_found
19
39
  end
40
+ rescue => e
41
+ render json: {
42
+ success: false,
43
+ error: "Failed to fetch room features",
44
+ message: e.message
45
+ }, status: :internal_server_error
20
46
  end
21
47
  end
@@ -1,4 +1,5 @@
1
1
  class Stay::Api::V1::PropertiesController < Stay::BaseApiController
2
+ include Stay::ImageResizerConcern
2
3
  before_action :authenticate_devise_api_token!
3
4
  before_action :set_property, only: [ :show, :update, :destroy ]
4
5
  before_action :check_create_access, only: [ :create, :update ]
@@ -2,13 +2,51 @@ class Stay::Api::V1::PropertyCategoriesController < Stay::BaseApiController
2
2
  before_action :authenticate_devise_api_token!
3
3
 
4
4
  def index
5
- @type = Stay::PropertyCategory.all
6
- return render json: { error: "no data found", success: false }, status: :not_found unless @type.any?
7
- render json: { data: ActiveModelSerializers::SerializableResource.new(@type, each_serializer: PropertyCategorySerializer), message: "data found", success: true }, status: :ok
5
+ categories = Stay::PropertyCategory.all
6
+
7
+ if categories.exists?
8
+ serialized_data = ActiveModelSerializers::SerializableResource.new(categories, each_serializer: PropertyCategorySerializer)
9
+ render json: {
10
+ data: serialized_data,
11
+ message: "Property categories found",
12
+ success: true
13
+ }, status: :ok
14
+ else
15
+ render json: {
16
+ data: [],
17
+ message: "No property categories found",
18
+ success: false
19
+ }, status: :not_found
20
+ end
21
+ rescue => e
22
+ render json: {
23
+ error: "Failed to fetch property categories",
24
+ message: e.message,
25
+ success: false
26
+ }, status: :internal_server_error
8
27
  end
9
28
 
10
29
  def show
11
- @type = Stay::PropertyCategory.find_by(id: params[:id])
12
- render json: { data: PropertyCategorySerializer.new(@type), message: "data found", success: true }, status: :ok
30
+ category = Stay::PropertyCategory.find_by(id: params[:id])
31
+
32
+ if category.present?
33
+ render json: {
34
+ data: PropertyCategorySerializer.new(category),
35
+ message: "Property category found",
36
+ success: true
37
+ }, status: :ok
38
+ else
39
+ render json: {
40
+ data: {},
41
+ message: "Property category not found",
42
+ success: false
43
+ }, status: :not_found
44
+ end
45
+ rescue => e
46
+ render json: {
47
+ error: "Failed to fetch property category",
48
+ message: e.message,
49
+ success: false
50
+ }, status: :internal_server_error
13
51
  end
14
52
  end
@@ -3,13 +3,13 @@ class Stay::Api::V1::PropertyTypesController < Stay::BaseApiController
3
3
 
4
4
  def index
5
5
  property_types = Stay::PropertyType.all
6
-
7
- if property_types.any?
6
+
7
+ if property_types.exists?
8
8
  serialized_data = ActiveModelSerializers::SerializableResource.new(property_types, each_serializer: PropertyTypeSerializer)
9
-
9
+
10
10
  render json: {
11
11
  data: serialized_data,
12
- message: "Data found",
12
+ message: "Property types found",
13
13
  success: true
14
14
  }, status: :ok
15
15
  else
@@ -17,14 +17,37 @@ class Stay::Api::V1::PropertyTypesController < Stay::BaseApiController
17
17
  data: [],
18
18
  message: "No property types found",
19
19
  success: false
20
- }, status: :ok
20
+ }, status: :not_found
21
21
  end
22
+ rescue => e
23
+ render json: {
24
+ error: "Failed to fetch property types",
25
+ message: e.message,
26
+ success: false
27
+ }, status: :internal_server_error
22
28
  end
23
-
24
29
 
25
30
  def show
26
- @type = Stay::PropertyType.find_by(id: params[:id])
27
- return render json: { error: "no data found", success: false }, status: :unprocessable_entity if @type.nil?
28
- render json: { data: PropertyTypeSerializer.new(@type), message: "data found", success: true }, status: :ok
31
+ type = Stay::PropertyType.find_by(id: params[:id])
32
+
33
+ if type.present?
34
+ render json: {
35
+ data: PropertyTypeSerializer.new(type),
36
+ message: "Property type found",
37
+ success: true
38
+ }, status: :ok
39
+ else
40
+ render json: {
41
+ data: {},
42
+ message: "Property type not found",
43
+ success: false
44
+ }, status: :not_found
45
+ end
46
+ rescue => e
47
+ render json: {
48
+ error: "Failed to fetch property type",
49
+ message: e.message,
50
+ success: false
51
+ }, status: :internal_server_error
29
52
  end
30
53
  end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Stay::Users::RegistrationsController < Devise::RegistrationsController
4
+ before_action :redirects
5
+
4
6
  # before_action :configure_sign_up_params, only: [:create]
5
7
  # before_action :configure_account_update_params, only: [:update]
6
8
 
@@ -59,4 +61,9 @@ class Stay::Users::RegistrationsController < Devise::RegistrationsController
59
61
  # def after_inactive_sign_up_path_for(resource)
60
62
  # super(resource)
61
63
  # end
64
+ private
65
+
66
+ def redirects
67
+ redirect_to "/admin/login"
68
+ end
62
69
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Stay::Users::SessionsController < Devise::SessionsController
4
+ before_action :redirects
4
5
  # before_action :configure_sign_in_params, only: [:create]
5
6
 
6
7
  # GET /resource/sign_in
@@ -24,4 +25,10 @@ class Stay::Users::SessionsController < Devise::SessionsController
24
25
  # def configure_sign_in_params
25
26
  # devise_parameter_sanitizer.permit(:sign_in, keys: [:attribute])
26
27
  # end
28
+
29
+ private
30
+
31
+ def redirects
32
+ redirect_to "/admin/login"
33
+ end
27
34
  end
@@ -3,8 +3,38 @@ module Stay
3
3
  include CurrencyHelper
4
4
  ICON_SIZE = 14
5
5
 
6
- def svg_icon(name:, classes: '', width:, height:)
7
- if name.ends_with?('.svg')
6
+ def badge_class(state)
7
+ case state
8
+ when "approved"
9
+ "green"
10
+ when "rejected"
11
+ "red"
12
+ when "accepted"
13
+ "orange"
14
+ when "confirmed"
15
+ "green"
16
+ when "booking_request"
17
+ "orange"
18
+ when "invoice_sent"
19
+ "orange"
20
+ when "canceled"
21
+ "red"
22
+ end
23
+ end
24
+
25
+ def icon_class(state)
26
+ case state
27
+ when "approved"
28
+ "icon-check-circle"
29
+ when "rejected"
30
+ "icon-times-circle"
31
+ else
32
+ "icon-exclamation-circle"
33
+ end
34
+ end
35
+
36
+ def svg_icon(name:, classes: "", width:, height:)
37
+ if name.ends_with?(".svg")
8
38
  icon_name = File.basename(name, File.extname(name))
9
39
  inline_svg_tag "#{icon_name}.svg", class: "icon-#{icon_name} #{classes}", size: "#{width}px*#{height}px"
10
40
  else
@@ -14,8 +44,8 @@ module Stay
14
44
 
15
45
  def active_badge(condition, options = {})
16
46
  label = options[:label]
17
- label ||= condition ? I18n.t('admin.say_yes') : I18n.t('admin.say_no')
18
- css_class = condition ? 'badge-active' : 'badge-inactive'
47
+ label ||= condition ? I18n.t("admin.say_yes") : I18n.t("admin.say_no")
48
+ css_class = condition ? "badge-active" : "badge-inactive"
19
49
 
20
50
  content_tag(:small, class: "badge badge-pill #{css_class}") do
21
51
  label
@@ -25,16 +55,16 @@ module Stay
25
55
  def link_to_with_icon(icon_name, text, url, options = {})
26
56
  options[:class] = (options[:class].to_s + " icon-link with-tip action-#{icon_name}").strip
27
57
  options[:title] = text if options[:no_text]
28
- text = options[:no_text] ? '' : content_tag(:span, text)
58
+ text = options[:no_text] ? "" : content_tag(:span, text)
29
59
  options.delete(:no_text)
30
60
  options[:width] ||= ICON_SIZE
31
61
  options[:height] ||= ICON_SIZE
32
62
  if icon_name
33
- icon = if icon_name.ends_with?('.svg')
63
+ icon = if icon_name.ends_with?(".svg")
34
64
  svg_icon(name: icon_name, classes: "#{'mr-2' unless text.empty?} icon icon-#{icon_name}", width: options[:width], height: options[:height])
35
- else
36
- content_tag(:span, '', class: "#{'mr-2' unless text.empty?} icon icon-#{icon_name}")
37
- end
65
+ else
66
+ content_tag(:span, "", class: "#{'mr-2' unless text.empty?} icon icon-#{icon_name}")
67
+ end
38
68
  text = "#{icon} #{text}"
39
69
  end
40
70
  link_to(text.html_safe, url, options)
@@ -43,17 +73,17 @@ module Stay
43
73
  def button_to_with_icon(icon_name, text, url, options = {})
44
74
  options[:class] = (options[:class].to_s + " icon-link with-tip action-#{icon_name}").strip
45
75
  options[:title] = text if options[:no_text]
46
- text = options[:no_text] ? '' : content_tag(:span, text)
76
+ text = options[:no_text] ? "" : content_tag(:span, text)
47
77
  options.delete(:no_text)
48
78
  options[:width] ||= ICON_SIZE
49
79
  options[:height] ||= ICON_SIZE
50
80
 
51
81
  if icon_name
52
- icon = if icon_name.ends_with?('.svg')
82
+ icon = if icon_name.ends_with?(".svg")
53
83
  svg_icon(name: icon_name, classes: "#{'mr-2' unless text.empty?} icon icon-#{icon_name}", width: options[:width], height: options[:height])
54
- else
55
- content_tag(:span, '', class: "#{'mr-2' unless text.empty?} icon icon-#{icon_name}")
56
- end
84
+ else
85
+ content_tag(:span, "", class: "#{'mr-2' unless text.empty?} icon icon-#{icon_name}")
86
+ end
57
87
  text = "#{icon} #{text}".html_safe
58
88
  end
59
89
 
@@ -2,6 +2,7 @@ module Stay
2
2
  class Property < ApplicationRecord
3
3
  ACTIVE_STATUS = "active".freeze
4
4
  APPROVED = "approved".freeze
5
+ PROPERTY = "property".freeze
5
6
  include Rails.application.routes.url_helpers
6
7
  include CurrencyHelper
7
8
  include Stay::ControllerHelpers::Currency
@@ -29,15 +30,15 @@ module Stay
29
30
  has_many :prices, through: :rooms
30
31
  has_many :line_items, through: :variants_including_master
31
32
  has_many :bookings
32
- belongs_to :property_category, class_name: "Stay::PropertyCategory", optional: true
33
+ belongs_to :property_category, class_name: "Stay::PropertyCategory"
33
34
  belongs_to :property_type, class_name: "Stay::PropertyType"
34
35
  has_many :property_amenities, class_name: "Stay::PropertyAmenity", dependent: :destroy
35
- has_many :amenities, through: :property_amenities, class_name: "Stay::Amenity"
36
+ has_many :property_type_amenities, -> { where(amenity_type: PROPERTY) }, through: :property_amenities, source: :amenity, class_name: "Stay::Amenity"
36
37
  has_many :additional_rules, class_name: "Stay::AdditionalRule", dependent: :destroy
37
38
  has_many :property_house_rules, class_name: "Stay::PropertyHouseRule", dependent: :destroy
38
39
  has_many :house_rules, through: :property_house_rules, class_name: "Stay::HouseRule"
39
40
  has_many :property_features, class_name: "Stay::PropertyFeature", dependent: :destroy
40
- has_many :features, through: :property_features, class_name: "Stay::Feature"
41
+ has_many :property_type_features,-> { where(feature_type: PROPERTY) },through: :property_features,source: :feature, class_name: "Stay::Feature"
41
42
  has_many :property_taxes, class_name: "Stay::PropertyTax", dependent: :destroy
42
43
  has_many :taxes, through: :property_taxes, class_name: "Stay::Tax"
43
44
  has_many :store_properties, class_name: "Stay::StoreProperty", dependent: :destroy
@@ -1,6 +1,7 @@
1
1
  module Stay
2
2
  class PropertyCategory < ApplicationRecord
3
3
  has_many :properties, class_name: "Stay::Property", dependent: :nullify
4
- validates :name, presence: true, uniqueness: { case_sensitive: false }, format: { without: /\s/, message: "must contain no spaces" }
4
+ validates :name, presence: true, uniqueness: { case_sensitive: false },
5
+ format: { without: /_/, message: "must not contain underscores" }
5
6
  end
6
7
  end
@@ -1,9 +1,9 @@
1
1
  module Stay
2
2
  class PropertyType < ApplicationRecord
3
- validates :name, presence: true, uniqueness: { case_sensitive: false }, format: { without: /\s/, error: "must contain no spaces" }
3
+ validates :name, presence: true, uniqueness: { case_sensitive: false },
4
+ format: { without: /_/, message: "must not contain underscores" }
4
5
 
5
-
6
- has_many :properties
6
+ has_many :properties, class_name: "Stay::Property", dependent: :nullify
7
7
  def self.ransackable_attributes(auth_object = nil)
8
8
  %w[id name created_at updated_at]
9
9
  end
@@ -1,7 +1,8 @@
1
1
  module Stay
2
2
  class RoomType < ApplicationRecord
3
- has_many :rooms, class_name: "Stay::Room"
3
+ has_many :rooms, class_name: "Stay::Room", dependent: :destroy
4
4
 
5
- validates :name, presence: true, uniqueness: { case_sensitive: false }, format: { without: /\s/, message: "must contain no spaces" }
5
+ validates :name, presence: true, uniqueness: { case_sensitive: false },
6
+ format: { without: /_/, message: "must not contain underscores" }
6
7
  end
7
8
  end
@@ -1,7 +1,4 @@
1
1
  class CreditCardSerializer < ActiveModel::Serializer
2
2
  attributes :id, :month, :year, :cc_number, :cc_type, :name, :user
3
3
  belongs_to :user
4
- # def user
5
- # UserSerializer.new(object.user)
6
- # end
7
4
  end
@@ -0,0 +1,3 @@
1
+ class FeatureSerializer < ActiveModel::Serializer
2
+ attributes :id, :name, :feature_type
3
+ end
@@ -1,7 +1,3 @@
1
1
  class PropertyCategorySerializer < ActiveModel::Serializer
2
- attributes :id, :name, :property_count
3
-
4
- def property_count
5
- object.properties.approved.active.count
6
- end
2
+ attributes :id, :name
7
3
  end
@@ -1,7 +1,7 @@
1
1
  class PropertyListingSerializer < ActiveModel::Serializer
2
2
  attributes :id, :title, :price_per_month, :place_images, :total_rooms,
3
3
  :availability_end, :availability_start, :extra_guest, :latitude,
4
- :longitude, :address, :city, :state, :country, :active, :property_state
4
+ :longitude, :address, :city, :state, :country, :active, :property_state, :slug
5
5
 
6
6
  belongs_to :property_category, Serializer: :PropertyCategorySerializer
7
7
  belongs_to :property_type, Serializer: :PropertyTypeSerializer
@@ -12,11 +12,11 @@ class PropertySerializer < ActiveModel::Serializer
12
12
  belongs_to :user
13
13
 
14
14
  def amenities
15
- ActiveModelSerializers::SerializableResource.new(object.amenities.property.uniq, each_serializer: AmenitySerializer)
15
+ ActiveModelSerializers::SerializableResource.new(object.property_type_amenities, each_serializer: AmenitySerializer)
16
16
  end
17
17
 
18
18
  def features
19
- ActiveModelSerializers::SerializableResource.new(object.features.property.uniq, each_serializer: PropertyFeatureSerializer)
19
+ ActiveModelSerializers::SerializableResource.new(object.property_type_features, each_serializer: FeatureSerializer)
20
20
  end
21
21
 
22
22
  def is_shared_property
@@ -1,7 +1,3 @@
1
1
  class PropertyTypeSerializer < ActiveModel::Serializer
2
- attributes :id, :name, :property_count
3
-
4
- def property_count
5
- object.properties.approved.active.count
6
- end
2
+ attributes :id, :name
7
3
  end
@@ -16,7 +16,7 @@ class RoomSerializer < ActiveModel::Serializer
16
16
  end
17
17
 
18
18
  def features
19
- ActiveModelSerializers::SerializableResource.new(object.features.distinct, each_serializer: PropertyFeatureSerializer)
19
+ ActiveModelSerializers::SerializableResource.new(object.features.distinct, each_serializer: FeatureSerializer)
20
20
  end
21
21
 
22
22
  def room_images
@@ -1,7 +1,8 @@
1
1
  class UserSerializer < ActiveModel::Serializer
2
2
  include Rails.application.routes.url_helpers
3
+
3
4
  attributes :id, :email, :first_name, :full_name, :last_name, :phone, :gender, :is_user, :is_host, :date_of_birth,
4
- :image, :whatsapp_notification, :sms_notification, :image_file_name,
5
+ :image, :image_file_name, :about_me, :country, :country_code
5
6
 
6
7
  def full_name
7
8
  object.full_name
@@ -23,14 +24,6 @@ class UserSerializer < ActiveModel::Serializer
23
24
  object.profile_image.url || nil
24
25
  end
25
26
 
26
- def whatsapp_notification
27
- object&.preferences.present? ? object&.preferences["whatsapp_notification"]: false
28
- end
29
-
30
- def sms_notification
31
- object&.preferences.present? ? object&.preferences["sms_notification"]: false
32
- end
33
-
34
27
  def image_file_name
35
28
  object.profile_image&.filename&.to_s || nil
36
29
  end
@@ -2,81 +2,81 @@
2
2
  <div class="card-header">
3
3
  <div class="row align-items-center">
4
4
  <div class="col-sm-6 col-12 mb-4 mb-sm-0">
5
- <!-- Title -->
6
- <h1 class="h3 mb-0 ls-tight">Properties</h1>
5
+ <h1 class="h3 mb-0 ls-tight">Properties</h1>
7
6
  </div>
8
7
  <div class="col-sm-6 col-12 text-sm-end">
9
- <div class="mx-n1">
8
+ <div class="mx-n1">
10
9
  <%= link_to new_admin_property_path, class:"btn d-inline-flex btn-sm btn-primary mx-1" do %>
11
- <span class=" pe-2">
10
+ <span class=" pe-2">
12
11
  <i class="bi bi-plus"></i>
13
- </span>
14
- <span>Create New</span>
12
+ </span>
13
+ <span>Create New</span>
15
14
  <%end%>
16
- </div>
15
+ </div>
17
16
  </div>
18
17
  <div class="row g-3 mt-4">
19
- <div class="col-lg-3">
18
+ <div class="col-3 mt-6">
20
19
  <%= search_form_for @q, url: admin_properties_path, method: :get, local: true, class: "input-group" do |f| %>
21
- <%= f.search_field :title_cont, placeholder: "Search by title", class: "form-control" %>
20
+ <%= f.search_field :title_cont, placeholder: "Search by Name", class: "form-control" %>
22
21
  <%= f.submit "Search", class: "btn btn-primary" %>
23
22
  <% end %>
24
23
  </div>
25
- </div>
24
+ <div class="col-9 d-flex justify-content-end">
25
+ <%= form_with url: admin_properties_path, method: :get, local: true, class: "row g-3 align-items-center" do |f| %>
26
+ <div class="col-auto mt-4">
27
+ <%= f.label :status, "Filter", class: "form-label" %>
28
+ <%= f.select :property_state, options_for_select([["All", ""], ["Waiting for Approval", "waiting_for_approval"], ["Approved", "approved"], ["Rejected", "rejected"]], params[:property_state]), {}, class: "form-select" %>
29
+ </div>
30
+ <div class="col-auto">
31
+ <%= f.submit "Filter", class: "btn btn-primary mt-3" %>
32
+ </div>
33
+ <% end %>
34
+ </div>
35
+ </div>
26
36
  </div>
27
37
  </div>
28
38
  <div class="table-responsive">
29
- <table class="table table-hover table-nowrap">
39
+ <table class="table table-hover table-nowrap text-center align-middle">
30
40
  <thead class="table-light">
31
- <tr>
32
- <th scope="col">Image</th>
33
- <th scope="col">Title</th>
34
- <th scope="col">Description</th>
35
- <th scope="col">Availability Start</th>
36
- <th scope="col">Availability End</th>
37
- <th scope="col">State</th>
38
- <th scope="col">Active</th>
39
- <th></th>
40
- </tr>
41
- </thead>
42
-
43
- <tbody>
44
- <% @properties.each do |property| %>
45
- <tr>
46
- <td>
47
- <%= image_tag main_app.url_for(property.cover_image), size: "25x25" if property.cover_image.attached? %>
48
- </td>
49
- <td><%= property.title %></td>
50
- <td><%= sanitize property.description %></td>
51
- <td><%= property.availability_start %></td>
52
- <td><%= property.availability_end %></td>
53
- <td><%= property.property_state %></td>
54
- <td>
55
- <%= property.active %>
56
- <span class="badge badge-lg badge-dot <%= property.active ? 'badge-primary' : 'badge-warning' %>">
57
- <i class="<%= property.active ? 'bg-primary' : 'bg-warning' %>"></i>
58
- </span>
59
- </td>
60
- <td class="text-end d-flex justify-content-end">
61
- <%= link_to edit_admin_property_path(property), class: 'btn d-inline-flex btn-sm btn-neutral mx-1' do %>
62
- <i class="bi bi-pencil"></i>
63
- <% end %>
64
- <%= link_to admin_property_path(property), class: 'btn d-inline-flex btn-sm btn-neutral mx-1' do %>
65
- <i class="bi bi-eye"></i>
66
- <% end %>
67
- <%= button_to admin_property_path(property), method: :delete, data: { turbo_method: :delete, turbo_confirm: 'Are you sure?' }, class: 'btn btn-sm btn-neutral mx-1 text-danger-hover' do %>
68
- <i class="bi bi-trash"></i>
69
- <% end %>
70
- </td>
71
- </tr>
72
- <% end %>
73
- </tbody>
74
- </table>
75
- </div>
76
- </div>
77
- <div class="card-footer border-0 py-5">
78
- <span class="text-muted text-sm">Showing <%= @properties.count > 0 ? @properties.count : 0%> items out of <%= @properties.count||0%> results found</span>
41
+ <tr>
42
+ <th scope="col">Sr.No</th>
43
+ <th scope="col">Title</th>
44
+ <th scope="col">Availability Start</th>
45
+ <th scope="col">Availability End</th>
46
+ <th scope="col">Status</th>
47
+ <th scope="col">Action</th>
48
+ </tr>
49
+ </thead>
50
+ <tbody>
51
+ <% @properties.each_with_index do |property, index| %>
52
+ <tr>
53
+ <td><%= index + 1 %></td>
54
+ <td><%= property.title %></td>
55
+ <td><%= property.availability_start && property.availability_start.strftime("%b %d, %Y") %></td>
56
+ <td><%= property.availability_end && property.availability_end.strftime("%b %d, %Y") %></td>
57
+ <td style = "color: <%= badge_class(property.property_state) %>">
58
+ <%= property.property_state.humanize %>
59
+ <span class="badge badge-lg badge-dot">
60
+ <i class="<%= icon_class(property.property_state) %>"></i>
61
+ </span>
62
+ </td>
63
+ <td class="d-flex justify-content-center">
64
+ <%= link_to edit_admin_property_path(property), class: 'btn d-inline-flex btn-sm btn-neutral mx-1' do %>
65
+ <i class="bi bi-pencil"></i>
66
+ <% end %>
67
+ <%= link_to admin_property_path(property), class: 'btn d-inline-flex btn-sm btn-neutral mx-1' do %>
68
+ <i class="bi bi-eye"></i>
69
+ <% end %>
70
+ <%= button_to admin_property_path(property), method: :delete, data: { turbo_method: :delete, turbo_confirm: 'Are you sure?' }, class: 'btn btn-sm btn-neutral mx-1 text-danger-hover' do %>
71
+ <i class="bi bi-trash"></i>
72
+ <% end %>
73
+ </td>
74
+ </tr>
75
+ <% end %>
76
+ </tbody>
77
+ </table>
78
+ </div>
79
79
  </div>
80
80
  <div class="d-flex justify-content-center">
81
- <%= paginate @properties %>
81
+ <%= paginate @properties %>
82
82
  </div>
@@ -2,68 +2,67 @@
2
2
  <div class="card-header">
3
3
  <div class="row align-items-center">
4
4
  <div class="col-sm-6 col-12 mb-4 mb-sm-0">
5
- <!-- Title -->
6
- <h1 class="h3 mb-0 ls-tight">Rooms</h1>
5
+ <!-- Title -->
6
+ <h1 class="h3 mb-0 ls-tight">Rooms</h1>
7
7
  </div>
8
8
  <!-- Actions -->
9
9
  <div class="col-sm-6 col-12 text-sm-end">
10
- <div class="mx-n1">
10
+ <div class="mx-n1">
11
11
  <%= link_to new_admin_property_room_path(@property), class:"btn d-inline-flex btn-sm btn-primary mx-1" do %>
12
- <span class=" pe-2">
12
+ <span class=" pe-2">
13
13
  <i class="bi bi-plus"></i>
14
- </span>
15
- <span>Create New</span>
14
+ </span>
15
+ <span>Create New</span>
16
16
  <%end%>
17
- </div>
17
+ </div>
18
18
  </div>
19
19
  </div>
20
20
  </div>
21
21
  <div class="table-responsive">
22
22
  <table class="table table-hover table-nowrap">
23
23
  <thead class="table-light">
24
- <tr>
25
- <th scope="col">Image</th>
26
- <th scope="col">Max Guests</th>
27
- <th scope="col">Price Per Night</th>
28
- <th scope="col">Room Type</th>
29
- <th scope="col">Booking Start</th>
30
- <th scope="col">Booking End</th>
31
- <th scope="col">Status</th>
32
-
33
- <th></th>
34
- </tr>
35
- </thead>
36
-
37
- <tbody>
38
- <% @rooms.each do |room| %>
39
- <tr>
40
- <td>
41
- <%= image_tag main_app.url_for(room.room_images.first), size: "25x25" if room.room_images.attached? %>
42
- </td>
43
- <td><%= room.max_guests %></td>
44
- <td><%= currency_symbol(current_currency) %> <%= room.price_per_month %></td>
45
- <td><%= room.room_type&.name %></td>
46
- <td><%= room.booking_start%></td>
47
- <td><%= room.booking_end%></td>
48
- <td><%= room.status%></td>
49
- <td class="text-end d-flex justify-content-end">
50
- <%= link_to edit_admin_property_room_path(@property, room), class: 'btn d-inline-flex btn-sm btn-neutral mx-1' do %>
51
- <i class="bi bi-pencil">Edit</i>
52
- <% end %>
53
- <%= link_to admin_property_room_path(@property, room), class: 'btn d-inline-flex btn-sm btn-neutral mx-1' do %>
54
- <i class="bi bi-eye">View</i>
55
- <% end %>
56
- <%= button_to admin_property_room_path(@property, room), method: :delete, data: { turbo_method: :delete, turbo_confirm: 'Are you sure?' }, class: 'btn btn-sm btn-neutral mx-1 text-danger-hover' do %>
57
- <i class="bi bi-trash">Destroy</i>
58
- <% end %>
59
- </td>
60
- </tr>
61
- <% end %>
62
- </tbody>
63
- </table>
64
- </div>
65
-
66
- <div class="card-footer border-0 py-5">
67
- <span class="text-muted text-sm">Showing 10 items out of 250 results found</span>
24
+ <tr>
25
+ <th scope="col">Image</th>
26
+ <th scope="col">Max Guests</th>
27
+ <th scope="col">Price Per Night</th>
28
+ <th scope="col">Room Type</th>
29
+ <th scope="col">Booking Start</th>
30
+ <th scope="col">Booking End</th>
31
+ <th scope="col">Status</th>
32
+ <th scope="col">Action</th>
33
+ <th></th>
34
+ </tr>
35
+ </thead>
36
+ <tbody>
37
+ <% @rooms.each do |room| %>
38
+ <tr>
39
+ <td>
40
+ <%= image_tag main_app.url_for(room.room_images.first.url), size: "25x25" if room.room_images.attached? %>
41
+ </td>
42
+ <td><%= room.max_guests %></td>
43
+ <td><%= currency_symbol(current_currency) %> <%= room.price_per_month %></td>
44
+ <td><%= room.room_type&.name %></td>
45
+ <td><%= room.booking_start&.strftime("%b %d, %Y")%></td>
46
+ <td><%= room.booking_end&.strftime("%b %d, %Y")%></td>
47
+ <td><%= room.status%></td>
48
+ <td class="text-end d-flex justify-content-end">
49
+ <%= link_to edit_admin_property_room_path(@property, room), class: 'btn d-inline-flex btn-sm btn-neutral mx-1' do %>
50
+ <i class="bi bi-pencil">Edit</i>
51
+ <% end %>
52
+ <%= link_to admin_property_room_path(@property, room), class: 'btn d-inline-flex btn-sm btn-neutral mx-1' do %>
53
+ <i class="bi bi-eye">View</i>
54
+ <% end %>
55
+ <%= button_to admin_property_room_path(@property, room), method: :delete, data: { turbo_method: :delete, turbo_confirm: 'Are you sure?' }, class: 'btn btn-sm btn-neutral mx-1 text-danger-hover' do %>
56
+ <i class="bi bi-trash">Destroy</i>
57
+ <% end %>
58
+ </td>
59
+ </tr>
60
+ <% end %>
61
+ </tbody>
62
+ </table>
68
63
  </div>
69
- </div>
64
+ </div>
65
+ <div class="d-flex justify-content-center">
66
+ <%= paginate @rooms %>
67
+ </div>
68
+
@@ -6,12 +6,15 @@
6
6
  <h2 class="text-xl font-semibold mb-0">User Details</h2>
7
7
  </div>
8
8
  <div class="card-body">
9
- <p><strong>Full Name: </strong><%= @user.first_name %> <%= @user.last_name %></p>
9
+ <%if @user.profile_image.attached?%>
10
+ <p><strong>Profile Image: </strong><%= image_tag main_app.url_for(@user.profile_image), size: "100x100", class: "mt-2" %></p>
11
+ <%end%>
12
+ <p><strong>Full Name: </strong><%= @user.full_name %></p>
10
13
  <p><strong>Email: </strong><%= @user.email %></p>
11
14
  <p><strong>Phone: </strong><%= @user.phone %></p>
12
- <p><strong>Date of Birth: </strong><%= @user.date_of_birth %></p>
15
+ <p><strong>Date of Birth: </strong><%= @user.date_of_birth&.strftime("%b %d, %Y") %></p>
13
16
  <p><strong>Gender: </strong><%= @user.gender %></p>
14
- <p><strong>Roles: </strong><%= @user.stay_roles.map(&:name).join(", ") %></p>
17
+ <p><strong>Role: </strong><%= @user.stay_roles.map(&:name).join(", ").capitalize %></p>
15
18
  <div class="d-flex mt-4 p-4 justify-content-center">
16
19
  <%= link_to 'Edit', edit_admin_user_path(@user), class: 'btn btn-warning me-3' %>
17
20
  <%= link_to 'Back', admin_users_path, class: 'btn btn-secondary' %>
@@ -0,0 +1,7 @@
1
+ class AddFieldToStayUsers < ActiveRecord::Migration[8.0]
2
+ def change
3
+ add_column :stay_users, :country, :string
4
+ add_column :stay_users, :about_me, :text
5
+ add_column :stay_users, :country_code, :string
6
+ end
7
+ end
data/lib/stay/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Stay
2
- VERSION = "0.1.11"
2
+ VERSION = "0.1.13"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stay_commerce
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.11
4
+ version: 0.1.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - w3villa-vikaspal
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-04-07 00:00:00.000000000 Z
11
+ date: 2025-04-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -479,6 +479,7 @@ files:
479
479
  - app/assets/stylesheets/stay/webpixels.css
480
480
  - app/channels/chat_channel.rb
481
481
  - app/controllers/concerns/stay/booking_validations.rb
482
+ - app/controllers/concerns/stay/image_resizer_concern.rb
482
483
  - app/controllers/concerns/stay/stripe_concern.rb
483
484
  - app/controllers/devise/api/tokens_controller.rb
484
485
  - app/controllers/stay/admin/addresses_controller.rb
@@ -610,13 +611,13 @@ files:
610
611
  - app/serializers/cancellation_policy_serializer.rb
611
612
  - app/serializers/chat_serializer.rb
612
613
  - app/serializers/credit_card_serializer.rb
614
+ - app/serializers/feature_serializer.rb
613
615
  - app/serializers/house_rules_serializer.rb
614
616
  - app/serializers/invoice_serializer.rb
615
617
  - app/serializers/last_5_messages_serializer.rb
616
618
  - app/serializers/line_item_serializer.rb
617
619
  - app/serializers/message_serializer.rb
618
620
  - app/serializers/property_category_serializer.rb
619
- - app/serializers/property_feature_serializer.rb
620
621
  - app/serializers/property_listing_serializer.rb
621
622
  - app/serializers/property_serializer.rb
622
623
  - app/serializers/property_type_serializer.rb
@@ -935,6 +936,7 @@ files:
935
936
  - db/migrate/20241205092943_create_friendly_id_slugs.rb
936
937
  - db/migrate/20241217044817_add_deleted_atto_stay_property.rb
937
938
  - db/migrate/20241230064432_modify_property_category_on_properties.rb
939
+ - db/migrate/20250407101335_add_field_to_stay_users.rb
938
940
  - db/seeds.rb
939
941
  - lib/stay.rb
940
942
  - lib/stay/controller_helpers/currency.rb
@@ -1,5 +0,0 @@
1
- class PropertyFeatureSerializer < ActiveModel::Serializer
2
- attributes :id , :name, :feature_type
3
- # has_many :properties, serializer: PropertySerializer
4
-
5
- end