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 24, 2024
1 parent 1d40c05 commit 60be888
Show file tree
Hide file tree
Showing 11 changed files with 466 additions and 330 deletions.
626 changes: 325 additions & 301 deletions Gemfile.lock

Large diffs are not rendered by default.

19 changes: 15 additions & 4 deletions app/controllers/spree/admin/kyc_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,23 @@ class KycController < Spree::Admin::ResourceController
# @overrided
def permitted_resource_params
kyc_result = calculate_kyc_value(params[:product])

params.require(:product).permit(:allowed_upload_later).merge(kyc: kyc_result)
params.require(:product)
.permit(
:allowed_upload_later,
public_metadata: { custom_kyc_fields: %i[key label attype] }
)
.merge(kyc: kyc_result)
end

def flash_error
flash[:error] = @object.errors.full_messages.join(', ')
def remove_kyc_field
key = params[:key]
@product.custom_kyc_fields.reject! { |field| field['key'] == key }

if @product.save
render json: { success: true }
else
render json: { success: false, errors: @product.errors.full_messages }, status: :unprocessable_entity
end
end

# @overrided
Expand Down
4 changes: 4 additions & 0 deletions app/models/concerns/spree_cm_commissioner/kyc_bitwise.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,9 @@ def available_social_contact_platforms
platforms = SpreeCmCommissioner::Guest.social_contact_platforms.keys
platforms.sort_by { |platform| platform == 'other' ? 1 : 0 }
end

def custom_kyc_fields
public_metadata['custom_kyc_fields'] || {}
end
end
end
20 changes: 16 additions & 4 deletions app/models/spree_cm_commissioner/guest.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ class Guest < SpreeCmCommissioner::Base
delegate :kyc, to: :line_item, allow_nil: true
delegate :allowed_upload_later?, to: :line_item, allow_nil: true

enum gender: { :other => 0, :male => 1, :female => 2 }
enum social_contact_platform: {
enum :gender, { :other => 0, :male => 1, :female => 2 }
enum :social_contact_platform, {
:other => 0,
:telegram => 1,
:facebook => 2,
:wechat => 3,
:whatsapp => 4,
:line => 5,
:viber => 6
}, _prefix: true
}, prefix: true

scope :complete, -> { joins(:line_item).merge(Spree::LineItem.complete) }
scope :complete_or_canceled, -> { joins(:line_item).merge(Spree::LineItem.complete_or_canceled) }
Expand Down 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) } && custom_kyc_fields_data_present?
end

def custom_kyc_fields_data_present?
return true if custom_kyc_fields.empty?

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

def allowed_checkout_for?(field) # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
Expand All @@ -77,6 +83,12 @@ def allowed_checkout_for?(field) # rubocop:disable Metrics/AbcSize, Metrics/Perc
false
end

# def require_custom_kyc_fields?
# return false if custom_kyc_fields.empty?

# custom_kyc_fields.any? { |entry| entry['value'].blank? || entry['value'].empty? }
# end

def require_kyc_field? # rubocop:disable Metrics/CyclomaticComplexity,Metrics/MethodLength
kyc_fields.any? do |field|
case field
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 datetime date bool].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 @@ -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,
:public_metadata

# temporary, once app release after cm-app v1.9.1, we no longer need this.
attribute :seat_number do |object|
Expand Down
10 changes: 10 additions & 0 deletions app/views/spree/admin/kyc/_custom_kyc_fields.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<div class="d-flex align-items-center mb-2">
<input type="text" class="form-control mx-1 my-1" name="product[public_metadata][custom_kyc_fields][][key]" value="<%= key %>" placeholder="Key" required>
<input type="text" class="form-control mx-1 my-1" name="product[public_metadata][custom_kyc_fields][][label]" value="<%= label %>" placeholder="Label" required>
<select class="form-control mx-1 my-1" name="product[public_metadata][custom_kyc_fields][][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/_custom_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">Custom kyc Fields</h5>
<button type="button" id="add-custom-kyc-field" class="btn btn-primary mx-1">Add New Field</button>
</div>

<div id="custom-kyc-fields" class="my-2">
<div class="form-group">
<div class="d-flex flex-column">
<% if @product.custom_kyc_fields&.empty? %>
<small class="form-text text-muted ml-1 my-2">
<%= raw I18n.t('custom_kyc_field.note') %>
</small>
<% else %>
<% @product.custom_kyc_fields&.each_with_index do |item, index| %>
<%= render partial: 'custom_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-custom-kyc-field');
const $kycFieldsContainer = document.getElementById('custom-kyc-fields');
const $form = document.querySelector('form');
const fieldTemplate = `<%= j(render partial: 'custom_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_kyc_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: 'custom_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"
custom_kyc_field:
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_kyc_field', to: 'kyc#remove_kyc_field'
end

resources :variant_guest_card_classes
Expand Down

0 comments on commit 60be888

Please sign in to comment.