From 6a62d4c9ef10aeea9bc0a79942833c2bcaea398f Mon Sep 17 00:00:00 2001 From: Oleg Hasjanov Date: Thu, 7 Dec 2023 11:20:48 +0200 Subject: [PATCH] fixed tests --- .../component.html.erb | 5 + .../delete_button_with_text/component.rb | 16 ++ .../rounded_checkbox/component.html.erb | 6 +- .../description/component.html.erb | 7 + .../password_field/description/component.rb | 20 +++ .../common/webpush/setting/component.html.erb | 4 + .../common/webpush/setting/component.rb | 16 ++ .../modals/change_offer/component.html.erb | 37 ++-- .../modals/change_offer/component.rb | 77 +++++++++ .../modals/change_offer_po/component.html.erb | 22 +-- .../modals/change_offer_po/component.rb | 70 +++++++- app/models/auction.rb | 1 - app/models/concerns/auction/searchable.rb | 159 ++++++++++++++++-- app/models/concerns/auction/sql_queriable.rb | 125 -------------- .../sorted_by_winning_offer_username.rb | 25 +++ .../auction/with_highest_offers_query.rb | 34 ++++ ...ith_max_offer_cents_for_english_auction.rb | 41 +++++ .../auction/with_offers_count_query.rb | 27 +++ .../queries/auction/with_user_offers_query.rb | 32 ++++ app/views/admin/auctions/_auction.html.erb | 1 + .../auctions/_deposit_participant.html.erb | 3 + .../auctions/_deposit_participants.html.erb | 5 + .../edit.html.erb | 0 .../new.html.erb | 0 app/views/users/_form.html.erb | 50 ++---- app/views/users/_sign_up_form.html.erb | 25 ++- app/views/users/_user_info.html.erb | 20 +-- app/views/users/show.html.erb | 4 +- test/components/common/button_to_test.rb | 93 +++++----- .../common/form/dropdown_input_test.rb | 6 +- 30 files changed, 634 insertions(+), 297 deletions(-) create mode 100644 app/components/common/buttons/delete_button_with_text/component.html.erb create mode 100644 app/components/common/buttons/delete_button_with_text/component.rb create mode 100644 app/components/common/form/password_field/description/component.html.erb create mode 100644 app/components/common/form/password_field/description/component.rb create mode 100644 app/components/common/webpush/setting/component.html.erb create mode 100644 app/components/common/webpush/setting/component.rb delete mode 100644 app/models/concerns/auction/sql_queriable.rb create mode 100644 app/models/queries/auction/sorted_by_winning_offer_username.rb create mode 100644 app/models/queries/auction/with_highest_offers_query.rb create mode 100644 app/models/queries/auction/with_max_offer_cents_for_english_auction.rb create mode 100644 app/models/queries/auction/with_offers_count_query.rb create mode 100644 app/models/queries/auction/with_user_offers_query.rb rename app/views/{autobider => autobider_deprecated}/edit.html.erb (100%) rename app/views/{autobider => autobider_deprecated}/new.html.erb (100%) diff --git a/app/components/common/buttons/delete_button_with_text/component.html.erb b/app/components/common/buttons/delete_button_with_text/component.html.erb new file mode 100644 index 000000000..555563902 --- /dev/null +++ b/app/components/common/buttons/delete_button_with_text/component.html.erb @@ -0,0 +1,5 @@ +<%= button_to @path, method: :delete, form: { data: { turbo_confirm: t("are_you_sure") } }, + class: "c-btn c-btn--ghost c-acount__button c-acount__button--icon", target: '_top' do %> + <%= render 'svg/trash' %> + <%= @text %> +<% end %> diff --git a/app/components/common/buttons/delete_button_with_text/component.rb b/app/components/common/buttons/delete_button_with_text/component.rb new file mode 100644 index 000000000..d67911564 --- /dev/null +++ b/app/components/common/buttons/delete_button_with_text/component.rb @@ -0,0 +1,16 @@ +module Common + module Buttons + module DeleteButtonWithText + class Component < ApplicationViewComponent + attr_reader :path, :text + + def initialize(path:, text:) + super + + @path = path + @text = text + end + end + end + end +end diff --git a/app/components/common/form/checkboxes/rounded_checkbox/component.html.erb b/app/components/common/form/checkboxes/rounded_checkbox/component.html.erb index 2f9021b4f..d6ecd76f1 100644 --- a/app/components/common/form/checkboxes/rounded_checkbox/component.html.erb +++ b/app/components/common/form/checkboxes/rounded_checkbox/component.html.erb @@ -1,2 +1,4 @@ -<%= @form.check_box @attribute, @options %> -
\ No newline at end of file + diff --git a/app/components/common/form/password_field/description/component.html.erb b/app/components/common/form/password_field/description/component.html.erb new file mode 100644 index 000000000..85c8a3e7d --- /dev/null +++ b/app/components/common/form/password_field/description/component.html.erb @@ -0,0 +1,7 @@ + diff --git a/app/components/common/form/password_field/description/component.rb b/app/components/common/form/password_field/description/component.rb new file mode 100644 index 000000000..0b34d0369 --- /dev/null +++ b/app/components/common/form/password_field/description/component.rb @@ -0,0 +1,20 @@ +module Common + module Form + module PasswordField + module Description + class Component < ApplicationViewComponent + attr_reader :form, :attribute, :minimum_password_length, :user + + def initialize(form:, attribute:, minimum_password_length:, user:) + super + + @form = form + @attribute = attribute + @minimum_password_length = minimum_password_length + @user = user + end + end + end + end + end +end \ No newline at end of file diff --git a/app/components/common/webpush/setting/component.html.erb b/app/components/common/webpush/setting/component.html.erb new file mode 100644 index 000000000..bb6178554 --- /dev/null +++ b/app/components/common/webpush/setting/component.html.erb @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/app/components/common/webpush/setting/component.rb b/app/components/common/webpush/setting/component.rb new file mode 100644 index 000000000..41315cace --- /dev/null +++ b/app/components/common/webpush/setting/component.rb @@ -0,0 +1,16 @@ +module Common + module Webpush + module Setting + class Component < ApplicationViewComponent + def data_data + { + controller: 'profile-webpush', + profile_webpush_target: 'checkbox', + profile_webpush_vapid_public_value: Rails.configuration.customization[:vapid_public], + action: 'change->push-notification#setupPushNotifications' + } + end + end + end + end +end diff --git a/app/components/modals/change_offer/component.html.erb b/app/components/modals/change_offer/component.html.erb index d88ff31ce..d025bb597 100644 --- a/app/components/modals/change_offer/component.html.erb +++ b/app/components/modals/change_offer/component.html.erb @@ -35,35 +35,32 @@ - <%= form_with model: @autobider, url: autobider_url, remote: true, - data: { turbo_frame: "_top", controller: 'form--autobider-submit form--autobider-validation', form__autobider_submit_target: 'form', form__autobider_validation_bid_min_value: @auction.min_bids_step.to_f }, - html: {id: ' autobid_form '} do |f| %> + <%= form_with **autobider_form_properties do |f| %>
<%= f.hidden_field :domain_name, value: offer.auction.domain_name %> <%= f.hidden_field :user_id, value: offer.user_id %> +
- <%= f.number_field :price, min: 0.0, step: 0.01, value: number_with_precision(@autobider.price.to_f,precision: 2), - disabled: @auction.finished? ? true : false, "data-action": "keydown->form--autobider-submit#validatePrice input->form--autobider-submit#validatingInputPrice", "data-form--autobider-submit-target": "price", "data-form--autobider-validation-target": "bidInput" %> + <%= component 'common/form/number_field', form: f, **number_field_properties %>
+
- - <%= t('english_offers.form.autobidder') %>: <%= t('english_offers.form.yep') %> + <%= component 'common/form/checkboxes/rounded_checkbox', form: f, **autobider_checkbox_properties %> + + + <%= t('english_offers.form.autobidder') %>: <%= t('english_offers.form.yep') %> +
<% end %> - <%= form_with model: @offer, url: url, id: 'english_offer_form', data: { turbo: false }, - data: { controller: 'autotax-counter', autotax_counter_template_value: t('english_offers.price_with_wat_template'), - autotax_counter_tax_value: "#{offer.billing_profile.present? ? offer.billing_profile.vat_rate : 0.0 }", - autotax_counter_defaulttemplate_value: t('offers.price_is_without_vat')} do |f| %> + <%= form_with **offer_form_properties do |f| %> <%= f.hidden_field :user_id, value: offer.user_id %> <%= f.hidden_field :auction_id, value: offer.auction_id %> +
@@ -71,11 +68,14 @@ <%= component 'modals/change_offer/number_form_field', offer_value: @auction.min_bids_step.to_f, offer_disabled: @auction.finished? %>
+
<% if offer.billing_profile.present? %> - <%= t('offers.price_with_wat', price: (@auction.min_bids_step.to_f * offer.billing_profile.vat_rate.to_f).to_f + @auction.min_bids_step.to_f, tax: offer.billing_profile.vat_rate * 100) %> + <%= t('offers.price_with_wat', + price: (@auction.min_bids_step.to_f * offer.billing_profile.vat_rate.to_f).to_f + @auction.min_bids_step.to_f, + tax: offer.billing_profile.vat_rate * 100) %> <% else %> <%= t('offers.price_is_without_vat') %> <% end %> @@ -84,12 +84,9 @@
<%= f.label :billing_profile, t('.bidder') %> - <%= f.select :billing_profile_id, - billing_profiles = BillingProfile.where(user_id: offer.user_id).collect { |b| [b.name, b.id, {'data-vat-rate' => b.vat_rate}] }, - {}, - class: billing_profiles.size == 1 ? "disabled" : "", - data: { action: 'change->autotax-counter#updateTax' } %> + <%= component 'common/form/dropdown_input', form: f, **billing_profile_dropdown_properties %>
+ <%= link_to t('new_billing_profile'), billing_profiles_path, class: 'c-modal__link c-modal__link--first-col', target: '_top' %> <% end %>
diff --git a/app/components/modals/change_offer/component.rb b/app/components/modals/change_offer/component.rb index e93fc01dd..5419a9755 100644 --- a/app/components/modals/change_offer/component.rb +++ b/app/components/modals/change_offer/component.rb @@ -47,6 +47,83 @@ def current_price(offer, current_user) content.html_safe end end + + def number_field_properties + { + attribute: :price, + options: { + min: 0.0, + step: 0.01, + value: number_with_precision(@autobider.price.to_f, precision: 2), + disabled: is_number_field_disabled?, + data: { + action: 'keydown->form--autobider-submit#validatePrice input->form--autobider-submit#validatingInputPrice', + form__autobider_submit_target: 'price', + form__autobider_validation_target: 'bidInput' + } + } + } + end + + def autobider_form_properties + { + model: @autobider, + url: autobider_url, + data: { + turbo_frame: '_top', + controller: 'form--autobider-submit form--autobider-validation', + form__autobider_submit_target: 'form', + form__autobider_validation_bid_min_value: @auction.min_bids_step.to_f + }, + html: { id: ' autobid_form ' } + } + end + + def autobider_checkbox_properties + { + attribute: :enable, + options: { + id: 'checkbox', + data: { + form__autobider_submit_target: 'checkbox', + action: 'change->form--autobider-submit#submitAutobider' + } + } + } + end + + def is_number_field_disabled? + @auction.finished? ? true : false + end + + def offer_form_properties + { + model: @offer, + url:, + id: 'english_offer_form', + data: { + turbo: false, + controller: 'autotax-counter', + autotax_counter_template_value: t('english_offers.price_with_wat_template'), + autotax_counter_tax_value: "#{offer.billing_profile.present? ? offer.billing_profile.vat_rate : 0.0}", + autotax_counter_defaulttemplate_value: t('offers.price_is_without_vat') + } + } + end + + def billing_profile_dropdown_properties + { + attribute: :billing_profile_id, + enum: billing_profiles = BillingProfile.where(user_id: offer.user_id).collect do |b| + [b.name, b.id, {'data-vat-rate' => b.vat_rate}] + end, + first_options: { }, + second_options: { + class: billing_profiles.size == 1 ? "disabled" : "", + data: { action: 'change->autotax-counter#updateTax' } + } + } + end end end end diff --git a/app/components/modals/change_offer_po/component.html.erb b/app/components/modals/change_offer_po/component.html.erb index daa723f26..06a322b26 100644 --- a/app/components/modals/change_offer_po/component.html.erb +++ b/app/components/modals/change_offer_po/component.html.erb @@ -32,10 +32,7 @@ - <%= form_with model: @offer, url: url, id: 'english_offer_form', data: { turbo: false }, - data: { controller: 'autotax-counter', autotax_counter_template_value: t('english_offers.price_with_wat_template'), - autotax_counter_tax_value: "#{offer.billing_profile.present? ? offer.billing_profile.vat_rate : 0.0 }", - autotax_counter_defaulttemplate_value: t('offers.price_is_without_vat')} do |f| %> + <%= form_with **offer_form_properties do |f| %> <%= f.hidden_field :user_id, value: offer.user_id %> <%= f.hidden_field :auction_id, value: offer.auction_id %>
@@ -44,10 +41,9 @@ <%= component 'modals/change_offer/number_form_field', offer_value: current_price, offer_disabled: @auction.finished? %> - - <%= t('.minimum_offer', minimum: Money.new(Setting.find_by(code: 'auction_minimum_offer').retrieve, - Setting.find_by(code: 'auction_currency').retrieve)) %> + <%= minimum_offer %>
+
@@ -61,11 +57,7 @@
<%= f.label :billing_profile, t('.bidder') %> - <%= f.select :billing_profile_id, - billing_profiles = BillingProfile.where(user_id: offer.user_id).collect { |b| [b.name, b.id, {'data-vat-rate' => b.vat_rate}] }, - {}, - class: billing_profiles.size == 1 ? "disabled" : "", - data: { action: 'change->autotax-counter#updateTax' } %> + <%= component 'common/form/dropdown_input', form: f, **billing_profile_dropdown_properties %>
<%= link_to t('new_billing_profile'), billing_profiles_path, class: 'c-modal__link c-modal__link--first-col', target: '_top' %> <% end %> @@ -79,11 +71,9 @@ <% if @auction.in_progress? %> <% end %> diff --git a/app/components/modals/change_offer_po/component.rb b/app/components/modals/change_offer_po/component.rb index 1992b8e52..0f6c02a0a 100644 --- a/app/components/modals/change_offer_po/component.rb +++ b/app/components/modals/change_offer_po/component.rb @@ -27,8 +27,74 @@ def offer_disabled? end def current_price - auction.offer_from_user(current_user).present? ? auction.current_price_from_user(current_user) : Money.new(Setting.find_by(code: 'auction_minimum_offer').retrieve) + if auction.offer_from_user(current_user).present? + auction.current_price_from_user(current_user) + else + Money.new(Setting.find_by(code: 'auction_minimum_offer').retrieve) + end + end + + def minimum_offer + I18n.t('.minimum_offer', minimum: Money.new(Setting.find_by(code: 'auction_minimum_offer').retrieve, + Setting.find_by(code: 'auction_currency').retrieve)) + end + + def offer_form_properties + { + model: @offer, + url:, + id: 'english_offer_form', + data: { + turbo: false, + controller: 'autotax-counter', + autotax_counter_template_value: t('english_offers.price_with_wat_template'), + autotax_counter_tax_value: "#{offer.billing_profile.present? ? offer.billing_profile.vat_rate : 0.0}", + autotax_counter_defaulttemplate_value: t('offers.price_is_without_vat') + } + } + end + + def billing_profile_dropdown_properties + { + attribute: :billing_profile_id, + enum: billing_profiles = BillingProfile.where(user_id: offer.user_id).collect do |b| + [b.name, b.id, { 'data-vat-rate' => b.vat_rate }] + end, + first_options: {}, + second_options: { + class: billing_profiles.size == 1 ? 'disabled' : '', + data: { action: 'change->autotax-counter#updateTax' } + } + } + end + + def delete_action_button_properties + { + type: 'delete', + href: offer_path(@auction.offer_from_user(current_user).uuid), + options: { + method: :delete, + form: { + data: { + turbo_confirm: t('.confirm_delete') + } + }, + target: '_top' + } + } + end + + def submit_form_button_properties + { + class: "c-btn c-btn--green", + id: 'bid_action', + form: 'english_offer_form', + style: 'cursor: pointer;', + data: { + turbo: false + } + } end end end -end \ No newline at end of file +end diff --git a/app/models/auction.rb b/app/models/auction.rb index 4fafe4360..ec70b9463 100644 --- a/app/models/auction.rb +++ b/app/models/auction.rb @@ -1,6 +1,5 @@ class Auction < ApplicationRecord # rubocop:disable Metrics include Presentable - include SqlQueriable include Searchable include PgSearch::Model diff --git a/app/models/concerns/auction/searchable.rb b/app/models/concerns/auction/searchable.rb index ac2475164..ff1179f34 100644 --- a/app/models/concerns/auction/searchable.rb +++ b/app/models/concerns/auction/searchable.rb @@ -6,6 +6,19 @@ module Auction::Searchable BLIND = 'blind' ENGLISH = 'english' + FILTERING_COLUMNS = %w[domain_name + starts_at + ends_at + highest_offer_cents + number_of_offers + turns_count + starting_price + min_bids_step + slipping_end + platform + requirement_deposit_in_cents + enable_deposit].freeze + included do scope :active, -> { where('starts_at <= ? AND ends_at >= ?', Time.now.utc, Time.now.utc) } scope :without_result, lambda { @@ -30,7 +43,7 @@ module Auction::Searchable end) scope :with_type, (lambda do |type| - if type.present? + if type.present? && type.in?([BLIND, ENGLISH]) return where(platform: [type, nil]) if type == BLIND where(platform: type) @@ -63,22 +76,14 @@ module Auction::Searchable end) end + # rubocop:disable Metrics/BlockLength + # rubocop:disable Metrics/MethodLength + # rubocop:disable Metrics/AbcSize class_methods do def search(params = {}, current_user = nil) param_list = %w[domain_name starts_at ends_at platform users_price] sort_column = params[:sort].presence_in(param_list) || 'domain_name' - sort_admin_column = params[:sort].presence_in(%w[domain_name - starts_at - ends_at - highest_offer_cents - number_of_offers - turns_count - starting_price - min_bids_step - slipping_end - platform - requirement_deposit_in_cents - enable_deposit]) || 'id' + sort_admin_column = params[:sort].presence_in(FILTERING_COLUMNS) || 'id' sort_direction = params[:direction].presence_in(%w[asc desc]) || 'desc' is_from_admin = params[:admin] == 'true' @@ -100,5 +105,133 @@ def search(params = {}, current_user = nil) query.order("#{is_from_admin ? sort_admin_column : sort_column} #{sort_direction} NULLS LAST") end end + + def with_user_offers(user_id) + Auction.from(with_user_offers_query(user_id)) + end + + def with_user_offers_query(user_id) + Queries::Auction::WithUserOffersQuery.call(user_id:) + + # sql = <<~SQL + # (WITH offers_subquery AS ( + # SELECT * + # FROM offers + # WHERE user_id = ? + # ) + # SELECT DISTINCT + # auctions.*, + # offers_subquery.cents AS users_offer_cents, + # offers_subquery.id AS users_offer_id, + # offers_subquery.uuid AS users_offer_uuid + # FROM auctions + # LEFT JOIN offers_subquery on auctions.id = offers_subquery.auction_id) AS auctions + # SQL + + # ActiveRecord::Base.sanitize_sql([sql, user_id]) + end + + def with_highest_offers + Auction.from(with_highest_offers_query) + end + + def with_highest_offers_query + Queries::Auction::WithHighestOffersQuery.call + + # <<~SQL + # (WITH offers_subquery AS ( + # SELECT DISTINCT on (uuid) offers.* + # FROM (SELECT auction_id, + # max(cents) over (PARTITION BY auction_id) as max_price, + # min(created_at) over (PARTITION BY auction_id, cents) as min_time + # FROM offers + # ) AS highest_offers + # INNER JOIN offers + # ON + # offers.cents = highest_offers.max_price AND + # offers.created_at = highest_offers.min_time AND + # offers.auction_id = highest_offers.auction_id) + # SELECT DISTINCT + # auctions.*, + # offers_subquery.cents AS highest_offer_cents, + # offers_subquery.id AS highest_offer_id, + # offers_subquery.uuid AS highest_offer_uuid, + # (SELECT COUNT(*) FROM offers where offers.auction_id = auctions.id) AS number_of_offers + # FROM auctions + # LEFT JOIN offers_subquery on auctions.id = offers_subquery.auction_id) AS auctions + # SQL + end + + def active_with_offers_count + Auction.active.from(with_offers_count_query) + end + + def with_offers_count_query + Queries::Auction::WithOffersCountQuery.call + + # <<~SQL + # (SELECT + # auctions.*, + # offers.cents, + # recent_offers.offers_count + # FROM auctions + # LEFT JOIN ( + # SELECT auction_id, + # MAX(updated_at) as last_updated_at, + # COUNT(*) as offers_count + # FROM offers + # GROUP BY auction_id + # ) AS recent_offers ON auctions.id = recent_offers.auction_id + # LEFT JOIN offers ON auctions.id = offers.auction_id AND offers.updated_at = recent_offers.last_updated_at) AS auctions + # SQL + end + + def with_max_offer_cents_for_english_auction(user = nil) + Queries::Auction::WithMaxOfferCentsForEnglishAuction.call(user: user) + + # if user + # joins(<<-SQL + # LEFT JOIN ( + # SELECT auction_id, MAX(cents) AS max_offer_cents + # FROM offers + # WHERE auction_id IN ( + # SELECT id FROM auctions WHERE platform = 1 + # UNION + # SELECT auction_id FROM offers WHERE user_id = #{user.id} AND auction_id IN (SELECT id FROM auctions WHERE platform IS NULL OR platform = 0) + # ) + # GROUP BY auction_id + # ) AS offers_subquery ON auctions.id = offers_subquery.auction_id + # SQL + # ) + # else + # joins(<<-SQL + # LEFT JOIN ( + # SELECT auction_id, MAX(cents) AS max_offer_cents + # FROM offers + # WHERE auction_id IN (SELECT id FROM auctions WHERE platform = 1) + # GROUP BY auction_id + # ) AS offers_subquery ON auctions.id = offers_subquery.auction_id + # SQL + # ) + # end + end + + def sorted_by_winning_offer_username + Queries::Auction::SortedByWinningOfferUsername.call + + # joins(<<-SQL + # LEFT JOIN ( + # SELECT offers.auction_id, offers.username + # FROM offers + # WHERE offers.cents = ( + # SELECT MAX(offers_inner.cents) + # FROM offers AS offers_inner + # WHERE offers_inner.auction_id = offers.auction_id + # ) + # GROUP BY offers.auction_id, offers.username + # ) AS offers_subquery ON auctions.id = offers_subquery.auction_id + # SQL + # ) + end end end diff --git a/app/models/concerns/auction/sql_queriable.rb b/app/models/concerns/auction/sql_queriable.rb deleted file mode 100644 index 63c049eb3..000000000 --- a/app/models/concerns/auction/sql_queriable.rb +++ /dev/null @@ -1,125 +0,0 @@ -# frozen_string_literal: true - -module Auction::SqlQueriable # rubocop:disable Metrics/ModuleLength - extend ActiveSupport::Concern - - class_methods do - def with_user_offers(user_id) - Auction.from(with_user_offers_query(user_id)) - end - - def with_user_offers_query(user_id) - sql = <<~SQL - (WITH offers_subquery AS ( - SELECT * - FROM offers - WHERE user_id = ? - ) - SELECT DISTINCT - auctions.*, - offers_subquery.cents AS users_offer_cents, - offers_subquery.id AS users_offer_id, - offers_subquery.uuid AS users_offer_uuid - FROM auctions - LEFT JOIN offers_subquery on auctions.id = offers_subquery.auction_id) AS auctions - SQL - - ActiveRecord::Base.sanitize_sql([sql, user_id]) - end - - def with_highest_offers - Auction.from(with_highest_offers_query) - end - - def with_highest_offers_query - <<~SQL - (WITH offers_subquery AS ( - SELECT DISTINCT on (uuid) offers.* - FROM (SELECT auction_id, - max(cents) over (PARTITION BY auction_id) as max_price, - min(created_at) over (PARTITION BY auction_id, cents) as min_time - FROM offers - ) AS highest_offers - INNER JOIN offers - ON - offers.cents = highest_offers.max_price AND - offers.created_at = highest_offers.min_time AND - offers.auction_id = highest_offers.auction_id) - SELECT DISTINCT - auctions.*, - offers_subquery.cents AS highest_offer_cents, - offers_subquery.id AS highest_offer_id, - offers_subquery.uuid AS highest_offer_uuid, - (SELECT COUNT(*) FROM offers where offers.auction_id = auctions.id) AS number_of_offers - FROM auctions - LEFT JOIN offers_subquery on auctions.id = offers_subquery.auction_id) AS auctions - SQL - end - - def active_with_offers_count - Auction.active.from(with_offers_count_query) - end - - def with_offers_count_query - <<~SQL - (SELECT - auctions.*, - offers.cents, - recent_offers.offers_count - FROM auctions - LEFT JOIN ( - SELECT auction_id, - MAX(updated_at) as last_updated_at, - COUNT(*) as offers_count - FROM offers - GROUP BY auction_id - ) AS recent_offers ON auctions.id = recent_offers.auction_id - LEFT JOIN offers ON auctions.id = offers.auction_id AND offers.updated_at = recent_offers.last_updated_at) AS auctions - SQL - end - - def with_max_offer_cents_for_english_auction(user = nil) - if user - joins(<<-SQL - LEFT JOIN ( - SELECT auction_id, MAX(cents) AS max_offer_cents - FROM offers - WHERE auction_id IN ( - SELECT id FROM auctions WHERE platform = 1 - UNION - SELECT auction_id FROM offers WHERE user_id = #{user.id} AND auction_id IN (SELECT id FROM auctions WHERE platform IS NULL OR platform = 0) - ) - GROUP BY auction_id - ) AS offers_subquery ON auctions.id = offers_subquery.auction_id - SQL - ) - else - joins(<<-SQL - LEFT JOIN ( - SELECT auction_id, MAX(cents) AS max_offer_cents - FROM offers - WHERE auction_id IN (SELECT id FROM auctions WHERE platform = 1) - GROUP BY auction_id - ) AS offers_subquery ON auctions.id = offers_subquery.auction_id - SQL - ) - end - end - - def sorted_by_winning_offer_username - joins(<<-SQL - LEFT JOIN ( - SELECT offers.auction_id, offers.username - FROM offers - WHERE offers.cents = ( - SELECT MAX(offers_inner.cents) - FROM offers AS offers_inner - WHERE offers_inner.auction_id = offers.auction_id - ) - GROUP BY offers.auction_id, offers.username - ) AS offers_subquery ON auctions.id = offers_subquery.auction_id - SQL - ) - end - end -end diff --git a/app/models/queries/auction/sorted_by_winning_offer_username.rb b/app/models/queries/auction/sorted_by_winning_offer_username.rb new file mode 100644 index 000000000..ec7af5938 --- /dev/null +++ b/app/models/queries/auction/sorted_by_winning_offer_username.rb @@ -0,0 +1,25 @@ +module Queries::Auction + class SortedByWinningOfferUsername + class << self + def call + new.call + end + end + + def call + joins(<<-SQL + LEFT JOIN ( + SELECT offers.auction_id, offers.username + FROM offers + WHERE offers.cents = ( + SELECT MAX(offers_inner.cents) + FROM offers AS offers_inner + WHERE offers_inner.auction_id = offers.auction_id + ) + GROUP BY offers.auction_id, offers.username + ) AS offers_subquery ON auctions.id = offers_subquery.auction_id + SQL + ) + end + end +end diff --git a/app/models/queries/auction/with_highest_offers_query.rb b/app/models/queries/auction/with_highest_offers_query.rb new file mode 100644 index 000000000..2e341e677 --- /dev/null +++ b/app/models/queries/auction/with_highest_offers_query.rb @@ -0,0 +1,34 @@ +module Queries::Auction + class WithHighestOffersQuery + class << self + def call + new.call + end + end + + def call + <<~SQL + (WITH offers_subquery AS ( + SELECT DISTINCT on (uuid) offers.* + FROM (SELECT auction_id, + max(cents) over (PARTITION BY auction_id) as max_price, + min(created_at) over (PARTITION BY auction_id, cents) as min_time + FROM offers + ) AS highest_offers + INNER JOIN offers + ON + offers.cents = highest_offers.max_price AND + offers.created_at = highest_offers.min_time AND + offers.auction_id = highest_offers.auction_id) + SELECT DISTINCT + auctions.*, + offers_subquery.cents AS highest_offer_cents, + offers_subquery.id AS highest_offer_id, + offers_subquery.uuid AS highest_offer_uuid, + (SELECT COUNT(*) FROM offers where offers.auction_id = auctions.id) AS number_of_offers + FROM auctions + LEFT JOIN offers_subquery on auctions.id = offers_subquery.auction_id) AS auctions + SQL + end + end +end diff --git a/app/models/queries/auction/with_max_offer_cents_for_english_auction.rb b/app/models/queries/auction/with_max_offer_cents_for_english_auction.rb new file mode 100644 index 000000000..00a29e3e4 --- /dev/null +++ b/app/models/queries/auction/with_max_offer_cents_for_english_auction.rb @@ -0,0 +1,41 @@ +module Queries::Auction + class WithMaxOfferCentsForEnglishAuction + class << self + def call(user: nil) + new(user:).call + end + end + + def initialize(user: nil) + @user = user + end + + def call + if @user + joins(<<-SQL + LEFT JOIN ( + SELECT auction_id, MAX(cents) AS max_offer_cents + FROM offers + WHERE auction_id IN ( + SELECT id FROM auctions WHERE platform = 1 + UNION + SELECT auction_id FROM offers WHERE user_id = #{@user.id} AND auction_id IN (SELECT id FROM auctions WHERE platform IS NULL OR platform = 0) + ) + GROUP BY auction_id + ) AS offers_subquery ON auctions.id = offers_subquery.auction_id + SQL + ) + else + joins(<<-SQL + LEFT JOIN ( + SELECT auction_id, MAX(cents) AS max_offer_cents + FROM offers + WHERE auction_id IN (SELECT id FROM auctions WHERE platform = 1) + GROUP BY auction_id + ) AS offers_subquery ON auctions.id = offers_subquery.auction_id + SQL + ) + end + end + end +end diff --git a/app/models/queries/auction/with_offers_count_query.rb b/app/models/queries/auction/with_offers_count_query.rb new file mode 100644 index 000000000..0a4d1a2ca --- /dev/null +++ b/app/models/queries/auction/with_offers_count_query.rb @@ -0,0 +1,27 @@ +module Queries::Auction + class WithOffersCountQuery + class << self + def call + new.call + end + end + + def call + <<~SQL + (SELECT + auctions.*, + offers.cents, + recent_offers.offers_count + FROM auctions + LEFT JOIN ( + SELECT auction_id, + MAX(updated_at) as last_updated_at, + COUNT(*) as offers_count + FROM offers + GROUP BY auction_id + ) AS recent_offers ON auctions.id = recent_offers.auction_id + LEFT JOIN offers ON auctions.id = offers.auction_id AND offers.updated_at = recent_offers.last_updated_at) AS auctions + SQL + end + end +end diff --git a/app/models/queries/auction/with_user_offers_query.rb b/app/models/queries/auction/with_user_offers_query.rb new file mode 100644 index 000000000..3e6c47a0a --- /dev/null +++ b/app/models/queries/auction/with_user_offers_query.rb @@ -0,0 +1,32 @@ +module Queries::Auction + class WithUserOffersQuery + class << self + def call(user_id:) + new(user_id:).call + end + end + + def initialize(user_id:) + @user_id = user_id + end + + def call + sql = <<~SQL + (WITH offers_subquery AS ( + SELECT * + FROM offers + WHERE user_id = ? + ) + SELECT DISTINCT + auctions.*, + offers_subquery.cents AS users_offer_cents, + offers_subquery.id AS users_offer_id, + offers_subquery.uuid AS users_offer_uuid + FROM auctions + LEFT JOIN offers_subquery on auctions.id = offers_subquery.auction_id) AS auctions + SQL + + ActiveRecord::Base.sanitize_sql([sql, @user_id]) + end + end +end diff --git a/app/views/admin/auctions/_auction.html.erb b/app/views/admin/auctions/_auction.html.erb index abcf640c7..2ec30eba8 100644 --- a/app/views/admin/auctions/_auction.html.erb +++ b/app/views/admin/auctions/_auction.html.erb @@ -4,6 +4,7 @@ + <%# TODO: Refactor this nested elements and move to the components %> <%= form.label "auction_ids_#{auction.id}", class: 'o-checkbox' do %> <%= form.check_box :auction_ids, { multiple: true, data: { form__bundle_checkbox_target: 'checkboxes' } }, auction.id, nil %>
diff --git a/app/views/admin/auctions/_deposit_participant.html.erb b/app/views/admin/auctions/_deposit_participant.html.erb index 49549f7da..457a1188e 100644 --- a/app/views/admin/auctions/_deposit_participant.html.erb +++ b/app/views/admin/auctions/_deposit_participant.html.erb @@ -2,9 +2,12 @@ <%= user.display_name %> <%= user.email %> + + <%# TODO: Refactor this nested elements and move to the components %> + diff --git a/app/views/admin/auctions/_deposit_participants.html.erb b/app/views/admin/auctions/_deposit_participants.html.erb index 2cf1266ef..f514a734a 100644 --- a/app/views/admin/auctions/_deposit_participants.html.erb +++ b/app/views/admin/auctions/_deposit_participants.html.erb @@ -6,9 +6,12 @@ action: 'input->form--debounce#search' } do |form| %>
<%= form.hidden_field :auction_id, value: auction.id %> + <%= form.search_field :search_string, value: params[:search_string], placeholder: 'Input user name, email or phone number', class: 'c-table__search__input js-table-search-dt' %>
+ + <%# TODO: Refactor this nested elements and move to the components %>
+ + <%# TODO: Refactor this nested elements and move to the components %>
diff --git a/app/views/users/_sign_up_form.html.erb b/app/views/users/_sign_up_form.html.erb index 7c7146e3a..30cfc2bc9 100644 --- a/app/views/users/_sign_up_form.html.erb +++ b/app/views/users/_sign_up_form.html.erb @@ -17,19 +17,15 @@ <%= component 'common/form/text_field', form: f, attribute: :surname, options: { autofocus: true } %>
- <%# TODO: Fixed the problem with nested label component %> - <%= f.label :password, class: "c-account__label-explain" do %> -
- <%= t('users.password') %> - <% if @user.persisted? || (@user.signed_in_with_identity_document? && @user.encrypted_password.blank?) %> - (<%= t('.you_can_leave_blank') %>) - <% end %> -
- <%= t('users.form.password_requirements', minimum: @minimum_password_length) if @minimum_password_length %> - <% end %> + <%= component 'common/form/password_field/description', + form: f, attribute: :password, + user: @user, minimum_password_length: @minimum_password_length %> <%= component 'common/form/password_field', form: f, attribute: :password, options: { autocomplete: "off" } %> - + + <% tag.span class:"c-account__input-explain" do %> + <%= t('users.form.password_requirements', minimum: @minimum_password_length) %> + <% end if @minimum_password_length %>
@@ -52,6 +48,7 @@
<%= component 'common/form/label', form: f, attribute: :country_code, title: t('users.country_code') %> + <% if @user.signed_in_with_identity_document? %>
<%= component 'common/form/text_field', form: f, attribute: :country_code, @@ -64,17 +61,17 @@ <% end %>
- <%# TODO: Modify checkbox label nested component to add support this variant of nested checkbox %> - <%# ================== %> <%= component 'common/form/checkboxes/checkbox_with_label', label_title: t('.daily_summary'), form: f, attribute: :daily_summary %> diff --git a/app/views/users/_user_info.html.erb b/app/views/users/_user_info.html.erb index 3b64e1019..667829f6c 100644 --- a/app/views/users/_user_info.html.erb +++ b/app/views/users/_user_info.html.erb @@ -38,16 +38,8 @@
- - @@ -63,15 +55,7 @@ <%= t('users.terms_and_conditions_link') %>
<%= component 'common/links/link_button', link_title: t(:billing), href: billing_profiles_path, color: 'ghost', options: { target: '_top' } %> - - <%# TODO: Resolve the problem with nested elements in button_to component %> - <%= button_to user_path(@user.uuid), method: :delete, form: { data: { turbo_confirm: t("are_you_sure") } }, - class: "c-btn c-btn--ghost c-acount__button c-acount__button--icon", target: '_top' do %> - <%= render 'svg/trash' %> - <%= t('users.show.delete') %> - <% end %> - <%# =================== %> - + <%= component 'common/buttons/delete_button_with_text', path: user_path(@user.uuid), text: t('users.show.delete') %>
diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb index d4c0f2ca0..a2336a051 100644 --- a/app/views/users/show.html.erb +++ b/app/views/users/show.html.erb @@ -1,5 +1,5 @@ <%= component 'common/hero', title: t('.title') %> - +
- + diff --git a/test/components/common/button_to_test.rb b/test/components/common/button_to_test.rb index a39571de0..3d765c165 100644 --- a/test/components/common/button_to_test.rb +++ b/test/components/common/button_to_test.rb @@ -1,15 +1,15 @@ require "test_helper" class ButtonToTest < ViewComponent::TestCase + # puts rendered_content def test_render_delete_component @user = users(:participant) @auction = auctions(:valid_with_offers) render_inline(Common::Buttons::ButtonTo::Component.new( - title_caption: 'Delete', + title_caption: 'Delete', color: 'ghost', href: route.offer_path(@auction.offer_from_user(@user).uuid), - color: 'ghost', method: :delete, - form_attributes: { data: { turbo_confirm: confirmation_iternalization } }) + options: { method: :delete, form_attributes: { data: { turbo_confirm: confirmation_iternalization } } }) ) assert_selector "form.button_to[data-turbo-confirm='#{confirmation_iternalization}'][method='post'][action='#{route.offer_path(@auction.offer_from_user(@user).uuid)}']" @@ -22,10 +22,9 @@ def test_render_nested_delete_component @auction = auctions(:valid_with_offers) render_inline(Common::Buttons::ButtonTo::Component.new( - title_caption: nil, + title_caption: nil, color: 'ghost', href: route.offer_path(@auction.offer_from_user(@user).uuid), - color: 'ghost', method: :delete, - form_attributes: { data: { turbo_confirm: confirmation_iternalization } }) + options: { method: :delete, form_attributes: { data: { turbo_confirm: confirmation_iternalization } } }) ) do 'Delete' end @@ -42,7 +41,7 @@ def test_render_post_component render_inline(Common::Buttons::ButtonTo::Component.new( title_caption: 'Create', href: route.offer_path(@auction.offer_from_user(@user).uuid), - color: 'ghost', method: :post) + color: 'ghost', options: { method: :post }) ) assert_selector "form.button_to[method='post'][action='#{route.offer_path(@auction.offer_from_user(@user).uuid)}']" @@ -56,50 +55,52 @@ def test_inline_button_colorize render_inline(Common::Buttons::ButtonTo::Component.new( title_caption: 'Delete', href: route.offer_path(@auction.offer_from_user(@user).uuid), - color: 'ghost', method: :delete, - form_attributes: { data: { turbo_confirm: confirmation_iternalization } }) + color: 'ghost', options: { + method: :delete, form_attributes: { data: { turbo_confirm: confirmation_iternalization } }}) ) - assert_selector "form.button_to button.c-login__button.c-btn.c-btn--ghost[type='submit']", text: route.offer_path(@auction.offer_from_user(@user).uuid) + + assert_selector "form.button_to button.c-login__button.c-btn.c-btn--ghost[type='submit']", text: 'Delete' + assert_selector "form.button_to[action='#{route.offer_path(@auction.offer_from_user(@user).uuid)}']" render_inline(Common::Buttons::ButtonTo::Component.new( - title_caption: 'Delete', + title_caption: 'Delete', color: 'blue-secondary', href: route.offer_path(@auction.offer_from_user(@user).uuid), - color: 'blue-secondary', method: :delete, - form_attributes: { data: { turbo_confirm: confirmation_iternalization } }) + options: { method: :delete, form_attributes: { data: { turbo_confirm: confirmation_iternalization } }}) ) - assert_selector "form.button_to button.c-login__button.c-btn.c-btn--blue-secondary[type='submit']", text: route.offer_path(@auction.offer_from_user(@user).uuid) + assert_selector "form.button_to button.c-login__button.c-btn.c-btn--blue-secondary[type='submit']", text: 'Delete' + assert_selector "form.button_to[action='#{route.offer_path(@auction.offer_from_user(@user).uuid)}']" render_inline(Common::Buttons::ButtonTo::Component.new( - title_caption: 'Delete', + title_caption: 'Delete', color: 'green', href: route.offer_path(@auction.offer_from_user(@user).uuid), - color: 'green', method: :delete, - form_attributes: { data: { turbo_confirm: confirmation_iternalization } }) + options: { method: :delete, form_attributes: { data: { turbo_confirm: confirmation_iternalization } }}) ) - assert_selector "form.button_to button.c-login__button.c-btn.c-btn--green[type='submit']", text: route.offer_path(@auction.offer_from_user(@user).uuid) + assert_selector "form.button_to button.c-login__button.c-btn.c-btn--green[type='submit']", text: 'Delete' + assert_selector "form.button_to[action='#{route.offer_path(@auction.offer_from_user(@user).uuid)}']" render_inline(Common::Buttons::ButtonTo::Component.new( - title_caption: 'Delete', + title_caption: 'Delete', color: 'blue', href: route.offer_path(@auction.offer_from_user(@user).uuid), - color: 'blue', method: :delete, - form_attributes: { data: { turbo_confirm: confirmation_iternalization } }) + options: { method: :delete, form_attributes: { data: { turbo_confirm: confirmation_iternalization } }}) ) - assert_selector "form.button_to button.c-login__button.c-btn.c-btn--blue[type='submit']", text: route.offer_path(@auction.offer_from_user(@user).uuid) + assert_selector "form.button_to button.c-login__button.c-btn.c-btn--blue[type='submit']", text: 'Delete' + assert_selector "form.button_to[action='#{route.offer_path(@auction.offer_from_user(@user).uuid)}']" render_inline(Common::Buttons::ButtonTo::Component.new( - title_caption: 'Delete', + title_caption: 'Delete', color: 'orange', href: route.offer_path(@auction.offer_from_user(@user).uuid), - color: 'orange', method: :delete, - form_attributes: { data: { turbo_confirm: confirmation_iternalization } }) + options: { method: :delete, form_attributes: { data: { turbo_confirm: confirmation_iternalization } } }) ) - assert_selector "form.button_to button.c-login__button.c-btn.c-btn--orange[type='submit']", text: route.offer_path(@auction.offer_from_user(@user).uuid) + assert_selector "form.button_to button.c-login__button.c-btn.c-btn--orange[type='submit']", text: 'Delete' + assert_selector "form.button_to[action='#{route.offer_path(@auction.offer_from_user(@user).uuid)}']" render_inline(Common::Buttons::ButtonTo::Component.new( - title_caption: 'Delete', + title_caption: 'Delete', color: 'black', href: route.offer_path(@auction.offer_from_user(@user).uuid), - color: 'black', method: :delete, - form_attributes: { data: { turbo_confirm: confirmation_iternalization } }) + options: { method: :delete, form_attributes: { data: { turbo_confirm: confirmation_iternalization } } }) ) - assert_selector "form.button_to button.c-login__button.c-btn.c-btn--black[type='submit']", text: route.offer_path(@auction.offer_from_user(@user).uuid) + assert_selector "form.button_to button.c-login__button.c-btn.c-btn--black[type='submit']", text: 'Delete' + assert_selector "form.button_to[action='#{route.offer_path(@auction.offer_from_user(@user).uuid)}']" end def test_nested_button_colorize @@ -107,60 +108,54 @@ def test_nested_button_colorize @auction = auctions(:valid_with_offers) render_inline(Common::Buttons::ButtonTo::Component.new( - title_caption: nil, - href: route.offer_path(@auction.offer_from_user(@user).uuid), - color: 'ghost', method: :delete, - form_attributes: { data: { turbo_confirm: confirmation_iternalization } }) + title_caption: nil, color: 'ghost', + href: route.offer_path(@auction.offer_from_user(@user).uuid), + options: { method: :delete, form_attributes: { data: { turbo_confirm: confirmation_iternalization } } }) ) do 'delete' end assert_selector "form.button_to button.c-btn--icon.c-btn.c-btn--ghost[type='submit']", text: 'delete' render_inline(Common::Buttons::ButtonTo::Component.new( - title_caption: nil, + title_caption: nil, color: 'blue-secondary', href: route.offer_path(@auction.offer_from_user(@user).uuid), - color: 'blue-secondary', method: :delete, - form_attributes: { data: { turbo_confirm: confirmation_iternalization } }) + options: { method: :delete, form_attributes: { data: { turbo_confirm: confirmation_iternalization } } }) ) do 'delete' end assert_selector "form.button_to button.c-btn--icon.c-btn.c-btn--blue-secondary[type='submit']", text: 'delete' render_inline(Common::Buttons::ButtonTo::Component.new( - title_caption: nil, + title_caption: nil, color: 'green', href: route.offer_path(@auction.offer_from_user(@user).uuid), - color: 'green', method: :delete, - form_attributes: { data: { turbo_confirm: confirmation_iternalization } }) + options: { method: :delete, form_attributes: { data: { turbo_confirm: confirmation_iternalization } } }) ) do 'delete' end assert_selector "form.button_to button.c-btn--icon.c-btn.c-btn--green[type='submit']", text: 'delete' render_inline(Common::Buttons::ButtonTo::Component.new( - title_caption: nil, + title_caption: nil, color: 'blue', href: route.offer_path(@auction.offer_from_user(@user).uuid), - color: 'blue', method: :delete, - form_attributes: { data: { turbo_confirm: confirmation_iternalization } }) + options: { method: :delete, form_attributes: { data: { turbo_confirm: confirmation_iternalization } } }) ) do 'delete' end assert_selector "form.button_to button.c-btn--icon.c-btn.c-btn--blue[type='submit']", text: 'delete' render_inline(Common::Buttons::ButtonTo::Component.new( - title_caption: nil, + title_caption: nil, color: 'orange', href: route.offer_path(@auction.offer_from_user(@user).uuid), - color: 'orange', method: :delete, - form_attributes: { data: { turbo_confirm: confirmation_iternalization } }) + options: { method: :delete, form_attributes: { data: { turbo_confirm: confirmation_iternalization } } }) ) do 'delete' end assert_selector "form.button_to button.c-btn--icon.c-btn.c-btn--orange[type='submit']", text: 'delete' render_inline(Common::Buttons::ButtonTo::Component.new( - title_caption: nil, + title_caption: nil, color: 'black', href: route.offer_path(@auction.offer_from_user(@user).uuid), - color: 'black', method: :delete, - form_attributes: { data: { turbo_confirm: confirmation_iternalization } }) + options: { method: :delete, form_attributes: { data: { turbo_confirm: confirmation_iternalization } } }) ) do 'delete' end diff --git a/test/components/common/form/dropdown_input_test.rb b/test/components/common/form/dropdown_input_test.rb index 074b379dc..82c272fa1 100644 --- a/test/components/common/form/dropdown_input_test.rb +++ b/test/components/common/form/dropdown_input_test.rb @@ -8,15 +8,15 @@ def test_render_component @invoice = invoices(:payable) form = ActionView::Helpers::FormBuilder.new(:invoice, @invoice, self, {}) + + # <%= @form.select @attribute, @enum, { **@first_options }, { **@second_options } %> render_inline(Common::Form::DropdownInput::Component.new( form: form, attribute: :billing_profile_id, enum: options_for_select(BillingProfile.where(user_id: @invoice.user_id).pluck(:name, :id), @invoice.billing_profile_id), - label: I18n.t('billing_profiles_name') + first_options: { include_blank: true }, second_options: { class: 'form-control' } )) - assert_selector 'label', text: 'Billing Profiles' assert_selector 'select#invoice_billing_profile_id[name="invoice[billing_profile_id]"]' - assert_selector 'select#invoice_billing_profile_id option[value="435320743"]', text: 'Joe John Participant' assert_selector 'select#invoice_billing_profile_id option[value="264178000"][selected="selected"]', text: 'ACME Inc.' assert_selector 'select#invoice_billing_profile_id option[value="338446587"]', text: 'Unused Profile'