Skip to content

Commit

Permalink
close #2174 dynamic kyc fields
Browse files Browse the repository at this point in the history
  • Loading branch information
panhachom committed Dec 23, 2024
1 parent 1d40c05 commit 433944e
Show file tree
Hide file tree
Showing 15 changed files with 147 additions and 27 deletions.
12 changes: 11 additions & 1 deletion app/controllers/spree/admin/kyc_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,18 @@ class KycController < Spree::Admin::ResourceController
# @overrided
def permitted_resource_params
kyc_result = calculate_kyc_value(params[:product])
params.require(:product).permit(:allowed_upload_later, dynamic_kyc: %i[key label attype]).merge(kyc: kyc_result)
end

def remove_field
key = params[:key]
@product.dynamic_kyc.reject! { |field| field['key'] == key }

params.require(:product).permit(:allowed_upload_later).merge(kyc: kyc_result)
if @product.save
render json: { success: true }
else
render json: { success: false, errors: @product.errors.full_messages }, status: :unprocessable_entity
end
end

def flash_error
Expand Down
3 changes: 2 additions & 1 deletion app/controllers/spree/api/v2/storefront/guests_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ def guest_params
:address,
:other_organization,
:expectation,
:upload_later
:upload_later,
:dynamic_field
)
end
end
Expand Down
8 changes: 7 additions & 1 deletion app/models/spree_cm_commissioner/guest.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,13 @@ def self.csv_importable_columns

# no validation for each field as we allow user to save data to model partially.
def allowed_checkout?
kyc_fields.all? { |field| allowed_checkout_for?(field) }
kyc_fields.all? { |field| allowed_checkout_for?(field) } && dynamic_fields_present?
end

def dynamic_fields_present?
return true if dynamic_field.empty?

dynamic_field.all? { |entry| entry['value'].present? }
end

def allowed_checkout_for?(field) # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
Expand Down
2 changes: 2 additions & 0 deletions app/models/spree_cm_commissioner/product_decorator.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
module SpreeCmCommissioner
module ProductDecorator
DYNAMIC_KYC_ATTRTYPES = %i[string integer float array datetime date].freeze

def self.prepended(base)
base.include SpreeCmCommissioner::ProductType
base.include SpreeCmCommissioner::KycBitwise
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def self.prepended(base)
base.has_one :venue, serializer: ::SpreeCmCommissioner::V2::Storefront::ProductPlaceSerializer

base.attributes :need_confirmation, :product_type, :kyc, :kyc_fields, :allowed_upload_later, :allow_anonymous_booking, :use_video_as_default
base.attributes :reveal_description, :discontinue_on, :public_metadata, :purchasable_on
base.attributes :reveal_description, :discontinue_on, :public_metadata, :purchasable_on, :dynamic_kyc

base.attribute :purchasable_on_app do |product|
product.purchasable_on == 'app' || product.purchasable_on == 'both'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ class GuestSerializer < BaseSerializer

attributes :first_name, :last_name, :dob, :gender, :kyc_fields, :other_occupation, :created_at, :updated_at,
:qr_data, :social_contact_platform, :social_contact, :available_social_contact_platforms,
:age, :emergency_contact, :other_organization, :expectation, :upload_later, :address, :formatted_bib_number, :phone_number
:age, :emergency_contact, :other_organization, :expectation, :upload_later, :address, :formatted_bib_number, :phone_number,
:dynamic_field

# temporary, once app release after cm-app v1.9.1, we no longer need this.
attribute :seat_number do |object|
Expand Down
12 changes: 12 additions & 0 deletions app/views/spree/admin/kyc/_dynamic_kyc_fields.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@


<div class="d-flex align-items-center mb-2">
<input type="text" class="form-control mx-1 my-1" name="product[dynamic_kyc][][key]" value="<%= key %>" placeholder="Key" required>
<input type="text" class="form-control mx-1 my-1" name="product[dynamic_kyc][][label]" value="<%= label %>" placeholder="Label" required>
<select class="form-control mx-1 my-1" name="product[dynamic_kyc][][attype]">
<% Spree::Product::DYNAMIC_KYC_ATTRTYPES.each do |type| %>
<option value="<%= type %>" <%= 'selected' if attype == type.to_s %>><%= type %></option>
<% end %>
</select>
<button type="button" class="btn btn-danger remove-field btn-sm">Remove</button>
</div>
68 changes: 68 additions & 0 deletions app/views/spree/admin/kyc/_dynamic_kyc_form.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<div class="d-flex align-items-center justify-content-between">
<h5 class="mx-1">Dynamic KYC Fields</h5>
<button type="button" id="add-dynamic-kyc-field" class="btn btn-primary mx-1">Add New Field</button>
</div>

<div id="dynamic-kyc-fields" class="my-2">
<div class="form-group">
<div class="d-flex flex-column">
<% if @product.dynamic_kyc.empty? %>
<small class="form-text text-muted ml-1 my-2">
<%= raw I18n.t('dynamic_kyc.note') %>
</small>
<% else %>
<% @product.dynamic_kyc.each_with_index do |item, index| %>
<%= render partial: 'dynamic_kyc_fields', locals: { key: item['key'], label: item['label'], attype: item['attype']} %>
<% end %>
<% end %>
</div>
</div>
</div>


<script>

document.addEventListener('DOMContentLoaded', () => {
const $addFieldButton = document.getElementById('add-dynamic-kyc-field');
const $kycFieldsContainer = document.getElementById('dynamic-kyc-fields');
const $form = document.querySelector('form');
const fieldTemplate = `<%= j(render partial: 'dynamic_kyc_fields', locals: { key: '', label: '', attype: '' }) %>`;

const getProductSlug = () => "<%= @product.slug %>";

const addField = () => {
$kycFieldsContainer.querySelector('.form-group .d-flex').insertAdjacentHTML('beforeend', fieldTemplate);
};

const removeField = (event) => {
if (event.target.classList.contains('remove-field')) {
const $fieldElement = event.target.closest('.d-flex');
const fieldKey = $fieldElement.querySelector('input[name*="[key]"]').value;

removeKycField(fieldKey)
.then(response => {
if (response.ok) {
$fieldElement.remove();
} else {
alert('Failed to remove the field');
}
});
}
};

const removeKycField = (fieldKey) => {
return fetch(`/admin/products/${getProductSlug()}/remove_field`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content
},
body: JSON.stringify({ key: fieldKey })
});
};

$addFieldButton.addEventListener('click', addField);
$kycFieldsContainer.addEventListener('click', removeField);
});

</script>
41 changes: 21 additions & 20 deletions app/views/spree/admin/kyc/edit.html.erb
Original file line number Diff line number Diff line change
@@ -1,31 +1,32 @@
<%= render partial: 'spree/admin/shared/product_tabs', locals: { current: :kyc } %>

<% content_for :page_actions do %>
<%= button_link_to Spree.t(:edit_icons),
admin_vectors_option_values_url,
{ icon: 'spree-icon.svg', id: 'admin-option-values-icons-index' }
%>
<% end %>

<%= form_with model: @product, url: update_kyc_admin_product_path(@product), method: :put do |form| %>
<small class="form-text text-muted">
<%= raw I18n.t('kyc.allowed_upload_later') %>
</small>
<div class="d-flex flex-column my-4">
<div class="form-check form-check-inline">
<%= form.field_container :allowed_upload_later do %>
<%= form.check_box :allowed_upload_later, class: 'form-check-input', checked: form.object.allowed_upload_later? %>
<%= form.label :allowed_upload_later, Spree.t(:allowed_upload_later), class: 'form-check-label' %>
<%= form.error_message_on :allowed_upload_later, class: 'text-danger' %>
<% end %>
</div>
<small class="form-text text-muted">
<%= raw I18n.t('kyc.allowed_upload_later') %>
</small>
<div class="d-flex flex-column my-4">
<div class="form-check form-check-inline">
<%= form.field_container :allowed_upload_later do %>
<%= form.check_box :allowed_upload_later, class: 'form-check-input', checked: form.object.allowed_upload_later? %>
<%= form.label :allowed_upload_later, Spree.t(:allowed_upload_later), class: 'form-check-label' %>
<%= form.error_message_on :allowed_upload_later, class: 'text-danger' %>
<% end %>
</div>
</div>
<small class="form-text text-muted">
<%= raw I18n.t('kyc.note') %>
</small>
<%= render partial: 'form', locals: { form: form } %>
<%= render partial: 'dynamic_kyc_form' %>

<small class="form-text text-muted">
<%= raw I18n.t('kyc.note') %>
</small>
<%= render partial: 'form', locals: { form: form } %>
<div class='form-actions' data-hook='buttons'>
<%= button Spree.t('actions.update'), 'save.svg', 'submit', { class: 'btn-success', data: { disable_with: "#{ Spree.t(:saving) }..." }} %>
</div>
<div class="form-actions" data-hook="buttons">
<%= button Spree.t('actions.update'), 'save.svg', 'submit', { class: 'btn-success', data: { disable_with: "#{ Spree.t(:saving) }..." }} %>
</div>
<% end %>


2 changes: 2 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,8 @@ en:
kyc:
note: "<b>Note:</b> Select Required KYC Field For this Product"
allowed_upload_later: "<b>Allow upload later:</b> Check this box if you allow to upload later"
dynamic_kyc:
note: "<b>Note:</b> No Dynamic KYC Field yet"
video_on_demand:
quality:
low: "Low (320p)"
Expand Down
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@
delete 'remove_metafield', to: 'metafields#remove_metafield'
get 'edit_kyc', to: 'kyc#edit'
put 'update_kyc', to: 'kyc#update'
delete 'remove_field', to: 'kyc#remove_field'
end

resources :variant_guest_card_classes
Expand Down
7 changes: 7 additions & 0 deletions db/migrate/20241219025924_add_dynamic_kyc_to_spree_product.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class AddDynamicKycToSpreeProduct < ActiveRecord::Migration[7.0]
def change
unless column_exists?(:spree_products, :dynamic_kyc)
add_column :spree_products, :dynamic_kyc, :jsonb, default: {}
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class AddDynamicFieldToSpreeCmCommissionerGuest < ActiveRecord::Migration[7.0]
def change
unless column_exists?(:cm_guests, :dynamic_field)
add_column :cm_guests, :dynamic_field, :jsonb, default: {}
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@
:seat_number,
:formatted_bib_number,
:phone_number,
:require_kyc_field
:require_kyc_field,
:dynamic_field
)
expect(subject[:data][:attributes][:qr_data]).to eq guest.token
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@
:use_video_as_default,
:purchasable_on,
:purchasable_on_app,
:purchasable_on_web
:purchasable_on_web,
:dynamic_kyc
)
end

Expand Down

0 comments on commit 433944e

Please sign in to comment.