← back to Dw Theme Compact Toolbar

snippets/section.flex-pdp.gift-recipient.liquid

283 lines

{% doc %}
  Copyright © 2025 Archetype Themes LP. All rights reserved.
  
  Renders the gift card recipient form for gift card products.
  
  This component provides a comprehensive form for customers to send gift cards to 
  recipients. It includes fields for recipient email, name, message, and delivery date, 
  with proper validation and error handling.
  
  Features:
  - Recipient email (required)
  - Recipient name (optional)
  - Personal message (optional, max 200 characters)
  - Delivery date (optional)
  - Form validation and error display
  - Automatic form clearing on successful submission

  @param {object} product - A product object
  
  @example
  {% render 'section.flex-pdp.gift-recipient', product: closest.product %}
{% enddoc %}

{%- liquid
  assign form_id = 'section.flex-pdp.form-' | append: section.id
-%}

{% stylesheet %}
	gift-recipient-form {
	  display: block;
	  position: relative;
	
	  & .recipient-fields {
	    display: none;
	  }
	}
{% endstylesheet %}

{%- liquid
  assign product = section.settings.product | default: product
  assign gift_card_recipient_control_flag = 'properties[__shopify_send_gift_card_to_recipient]'

  capture checkbox_label
    render 'element.text', as: 'span', variant: 'body-md', locale: 'actions.send_as_gift', text: 'I want to send this as a gift'
  endcapture

  capture input_email_id
    render 'utility.id', name: 'properties[Recipient email]'
  endcapture

  capture input_email_label
    render 'utility.translate', key: 'labels.recipient_email', fallback: 'Recipient email'
  endcapture

  capture input_name_id
    render 'utility.id', name: 'properties[Recipient name]'
  endcapture

  capture input_name_label
    render 'utility.translate', key: 'labels.recipient_name_optional', fallback: 'Recipient name (optional)'
  endcapture

  capture input_message_id
    render 'utility.id', name: 'properties[Message]'
  endcapture

  capture input_message_label
    render 'utility.translate', key: 'labels.message_optional', fallback: 'Message (optional)'
  endcapture

  capture input_date_id
    render 'utility.id', name: 'properties[Send on]'
  endcapture

  capture input_date_label
    render 'utility.translate', key: 'labels.recipient_send_on', fallback: 'Send on (optional)'
  endcapture

  assign max_chars_message = 200
  capture fallback_max_chars_message_rendered
    echo max_chars_message
    echo ' characters max'
  endcapture

  assign info_max_characters = 'info.max_characters' | t: max_chars: max_chars_message
  capture max_chars_message_rendered
    render 'utility.translate', t: info_max_characters, fallback: fallback_max_chars_message_rendered
  endcapture
-%}

{%- capture input_email_attributes -%}
  {% if form.errors contains 'email' %}
    aria-invalid="true"
    aria-describedby="RecipientForm-email-error-{{ section.id }}"
  {% endif %}
{%- endcapture -%}

{%- capture input_name_attributes -%}
  {% if form.errors contains 'name' %}
    aria-invalid="true"
    aria-describedby="RecipientForm-name-error-{{ section.id }}"
  {% endif %}
{%- endcapture -%}

{%- capture input_date_attributes -%}
  autocomplete="send_on"
  pattern="\d{4}-\d{2}-\d{2}"
  {% if form.errors contains 'send_on' %}
    aria-invalid="true"
    aria-describedby="RecipientForm-send_on-error-{{ section.id }}"
  {% endif %}
{%- endcapture -%}

{%- capture input_message_attributes -%}
  maxlength="{{ max_chars_message }}"
  aria-label="{{ input_message_label }}"
  {% if form.errors contains 'message' %}
    aria-invalid="true"
    aria-describedby="RecipientForm-message-error-{{ section.id }}"
  {% endif %}
{%- endcapture -%}

{% capture input_message_content %}
  {%- render 'element.input',
    multiline: 10,
    id: input_message_id,
    classlist: 'gift-recipient-form__message',
    name: 'properties[Message]',
    placeholder: input_message_label,
    label: input_message_label,
    attributes: input_message_attributes,
    value: form.message,
    full: true
  -%}

  {%- render 'element.text', variant: 'body-sm', text: max_chars_message_rendered, style: 'opacity: 0.7;' -%}
{% endcapture %}

{% capture form_inputs %}
  {% form 'product', product %}
    {% capture form_inputs_content %}
      {%- render 'element.input',
        classlist: 'gift-recipient-form__email',
        id: input_email_id,
        type: 'email',
        placeholder: input_email_label,
        label: input_email_label,
        name: 'properties[Recipient email]',
        value: form.email,
        attributes: input_email_attributes,
        full: true
      -%}

      {%- render 'element.input',
        classlist: 'gift-recipient-form__name',
        id: input_name_id,
        type: 'text',
        placeholder: input_name_label,
        label: input_name_label,
        name: 'properties[Recipient name]',
        value: form.name,
        attributes: input_name_attributes,
        full: true
      -%}

      {% render 'layout.stack', gap: 'xs', slot: input_message_content %}

      {%- render 'element.input',
        classlist: 'gift-recipient-form__date',
        id: input_date_id,
        type: 'date',
        label: input_date_label,
        placeholder: input_date_label,
        name: 'properties[Send on]',
        value: form.send_on,
        attributes: input_date_attributes,
        full: true
      -%}
    {% endcapture %}
  {% endform %}
{% endcapture %}

{% capture main_content %}
  {%- render 'element.checkbox',
    classlist: 'gift-recipient-form__checkbox',
    name: gift_card_recipient_control_flag,
    label: checkbox_label
  -%}
  
  {% render 'layout.stack', gap: 'sm', slot: form_inputs_content, classlist: 'recipient-fields' %}
{% endcapture %}


<gift-recipient-form data-form-id="{{ form_id }}">

  {% render 'layout.stack', gap: 'sm', slot: main_content %}

  <input
    type="hidden"
    name="{{ gift_card_recipient_control_flag }}"
    value="if_present"
    id="Recipient-Control-{{ section.id }}"
    disabled
  >
</gift-recipient-form>

{% javascript %}
  class GiftRecipientForm extends HTMLElement {
    constructor() {
      super()
      this.checkboxInput = this.querySelector('.gift-recipient-form__checkbox input[type="checkbox"]')
      this.emailInput = this.querySelector('.gift-recipient-form__email')
      this.nameInput = this.querySelector('.gift-recipient-form__name')
      this.messageInput = this.querySelector('.gift-recipient-form__message')
      this.dateInput = this.querySelector('.gift-recipient-form__date')
      this.addEventListener('change', () => this.onChange())
      this.recipientFields = this.querySelector('.recipient-fields')

      this.checkboxInput.addEventListener('change', () => {
        event.stopPropagation()
        this.recipientFields.style.display = this.checkboxInput.checked ? 'block' : 'none'
      })
    }

    connectedCallback() {
      this.abortController = new AbortController()

      document.addEventListener('product-form:submit:error', (event) => {
        if (event.target.form.id === this.dataset.formId) {
          this.displayErrorMessage(event.detail.errorMessage)
        }
      }, { signal: this.abortController.signal })

      document.addEventListener('product-form:submit:success', (event) => {
        if (event.target.form.id === this.dataset.formId) {
          this.clearInputFields()
          this.clearErrorMessage()
        }
      }, { signal: this.abortController.signal })
    }

    disconnectedCallback() {
      this.abortController.abort()
    }

    onChange() {
      if (!this.checkboxInput.checked) {
        this.clearInputFields()
        this.clearErrorMessage()
      }
    }

    clearInputFields() {
      for (const element of this.querySelectorAll('input')) {
        element.value = ''
      }
    }

    displayErrorMessage(body) {
      this.clearErrorMessage()
      if (body) {
        return Object.entries(body).forEach(([key, value]) => {
          const inputElement = this[`${key}Input`]
          if (!inputElement) {
            return
          }

          inputElement.setAttribute('aria-invalid', true)
        })
      }
    }

    clearErrorMessage() {
      for (const inputElement of this.querySelectorAll('input')) {
        inputElement.setAttribute('aria-invalid', false)
        inputElement.removeAttribute('aria-describedby')
      }
    }
  }

  customElements.define('gift-recipient-form', GiftRecipientForm)
{% endjavascript %}