Pixfizz Knowledge Base
  • 👋Welcome to Pixfizz
  • Overview
    • 💡Getting Started Checklist
  • Installation to Ecommerce stores
    • Pixfizz Ecommerce
      • Setup Required Custom Fields
      • Setup Required Custom Types
      • Shopper FAQs
        • How to Add a Custom Font to my Site
      • Liquid Documentation
    • Shopify Ecommerce
      • 💫How to get started
      • 🛠️Shopify Installation
      • 🛠️Shopify Installation - Variable Pages
        • Add Saved Projects Functionality
      • Linking products to shopify
    • ETSY
      • Setting Up Listings in Pixfizz
      • Setting up SKU's in Etsy & Pixfizz
  • Production Management
    • What Output Options are there for Production?
    • Production PDF versions
    • Add Order Codes to Production Files
    • Add Dynamic Barcode
    • Add a QR Code Element
      • QR Code Content element substitutions
    • Receipt of Orders
    • Custom fulfillment folder name
    • Ability to provide file content directly in _additional_files.json
    • Add Trim Marks to Production Files
    • File Upload variants and template options
    • Film Processing
  • Fulfillment Partners
    • Request Access to a Catalog
    • Publishing Products from a Catalog to your Site
    • Change Background Color of Preview Images
    • Mixam
      • About Mixam
      • Get Started with Mixam
      • Product Catalog
      • Request a Print Sample from Mixam (optional)
      • Fullfilment of Mixam Products
      • Linking to Mixam API
    • Navitor
      • Product Catalog
    • LA Cameras
      • Product Catalog
  • eCommerce CMS
    • Setup Shipping Services
    • How to Setup Taxes
      • Setup Tax Exempt Users
    • Extra Fees
    • Custom Web Components
      • <px-image-upload>
      • <px-date-selector>
    • Import promocodes into Pixfizz admin
    • FAQ
      • Hide a page from Search Engine Crawls
    • Google Analytics & Google Tag Manager
      • Setup New Account
      • Setup eCommerce Events in Pixfizz CMS
  • PRODUCT SETUP & DESIGN
    • Product Pricing
    • How to Create Templates
      • XML Definition Parameters
      • XML Set Parameters
      • XML Page Parameters
      • PDF Layers
      • Define page break on Layflat spreads
      • Calendar Setup
        • Calendar Week Starting with Sunday
        • Calendar Week Starting with Monday
    • Setup Collections to Publish Products
      • How to add Filters to your Collections
    • File Artwork
    • MSP HUB content
      • Graduation Text Element Substitutions
    • Sample Templates
      • Photo Prints
      • Cards (Folded)
      • Canvas Prints
      • Framed Prints
      • Notebooks
      • Drinkware
      • Clothing
      • Photo Albums
      • Layflat Photo Albums
    • Designs
      • How to Build a Card - Part 1: Implement Design
      • How to Build a Card - Part 2: Add Design Flexibility with Substitutions
      • How to Build a Photo Album - Layouts
      • How to Build a Photo Album - Trim Marks on Cover & Page
      • Template Business Card EU
    • Template and Design Options
    • Help Guide for the Editor
    • User Editing Options
      • Standard Editor
      • PX Preview
  • Admin Guide
    • Domains
    • Setup reCAPTCHA
    • Analytics
    • 📪Dashboard
    • Orders
      • Orders
      • Abandoned Carts
      • Production Files
      • Projects
    • Users
      • Users
        • User Information
        • Merging Anonymous Users
        • Change Password
        • Custom Fields
        • Promocodes (Single User)
        • Last Orders
        • Projects
        • Galleries
        • Calendars
        • Addresses
      • Invites
    • Galleries
      • Galleries
      • Organize/Tag Galleries
      • Public galleries image source for upload dialog
    • Products
      • Products
      • Product Attributes
      • Templates
        • Designs
      • Collections
      • Fonts
      • Font Palettes
      • Color Palettes
      • Element Substitutions
        • Element substitutions on text variants replacing “Target Element Name”**
      • Calendars
      • Price Variables
    • Shipping
      • Shipping Services
        • Ranking Weight for Shipping Options
      • Packaging
      • Shipping Address of Origin
      • Taxes
      • Addresses
      • Liquid support for shipping formulas
    • Marketing
      • Promocodes
    • Website
      • Pages
      • Layouts
      • Snippets
      • Assets
      • Special Web Components
        • Date Selector Component
        • Dynamic Previews
    • Settings
      • General
        • Website Configuration
        • Domain Hosting
        • Facebook Integration
        • Instagram Integration
        • Dropbox Integration
          • Dropbox integration for upload dialog
        • API settings
        • Watermark
      • Email Notifications
        • Email Notification Settings
        • Order Pending Confirmation Email
        • Order Payment Confirmation Email
        • Order Shipped Email
        • Signup Email
        • Password Reset Email
        • Abandoned Cart Email
        • Refer A Friend Email
      • Design Tool
        • Name
        • Logo
        • Favicon
        • Brand Color
        • Page Title
        • Use New Editor
        • Autofill Button
        • Launch Into Autofill
        • Captions
        • Unedited Warning
        • Image Flip
        • Alignment Aids
          • Alignment highlights
        • Two Page Spread
        • Auto Cropping
        • Crop Bleed
        • Asymmetrical bleeds and margins
        • Image Effects
        • Image Rotation
        • Edit cut print projects
        • Placeholder Rotation
        • Image Borders
        • Background Colors
        • Border Radius
        • Image Size Overlay
        • Project Options
        • Display Price
        • Copy Shared Projects
        • Cut Print Mode
        • Multi-Image Uploader
        • Large Format / Wall-Art
        • Highlight Editable Elements
        • Use Mapped Preview
        • Homepage URL
        • Cart Page Name
        • Account Page Name
        • Upload Size Limit
        • Default Font
        • Default Font Palette
        • Default Font Size
        • Default Text Color Palette
        • Default Image Border Color Palette
        • Background Color Palette
        • Google Tag Manager ID
        • Image Sources
        • Categories
        • Quicksave Prompt Delay
        • Help URL
        • Resolution Warning
        • Text Cropping Warning
        • Multitext Custom Fields
        • Custom JS
        • Admin Custom JS
        • Custom CSS
        • Fill Multiple Placeholders with a Print
        • Auto-Generate Previews from Design Products
        • Shrink-to-Fit for px-Image-Upload
        • PDF Import Panel
      • Translations
      • OAuth
      • Documentation
    • Payment Gateways
      • Stripe Webhook for callbacks
      • PayU Money (India) Callback URL
  • Use Cases
    • 🎨For Designers
    • 🖥️For Developers
  • 🔗API
    • 🖼️Dynamic Design Previews
  • Setup GA4 eCommerce Analytics
  • 🌐SEO
  • Structured Data
    • Product Pages
    • Shop Pages: ItemList
  • INTEGRATIONS
    • Dropbox
    • Darkroom
    • Stamped.io
    • Shippo
      • Shippo Setup
    • Tracking Unlimited
      • Setup
      • Product Setup
        • Product Code
        • Product Options
        • Shipping Services
    • Syncovery
      • Installation
      • Profile Setup
      • DPOF Profile Setup
      • Scheduler
      • Advanced Workflow Automation
    • Indisposable
    • Klaviyo
  • Group 1
    • Form to update Order Status
Powered by GitBook
On this page
  1. Installation to Ecommerce stores
  2. Shopify Ecommerce
  3. Shopify Installation - Variable Pages

Add Saved Projects Functionality

How to implement the saved projects page for Shopify.

Create the Pixfizz Products API page

Add a new template under the theme code edit page. Name it page.pixfizz-product-api.liquid

Copy the below code to this page

{% layout none %}
{
{%- comment %}
  On sites with many non-pixfizz products, it makes sense from a performance perspective
  to use a dedicated collections for pixfizz products, to be able to iterate only through them
  instead of through all live products with `collections.all`.
{%- endcomment %}
{% paginate collections.all.products by 50 %}
  {% for product in collections.all.products %}
    "{{ product.id }}": {
      "handle": {{ product.handle | json }},
      "title": {{ product.title | json }},
      "integration_type": {{ product.metafields.pixfizz.integration_type | json }},
      "pixfizz_sku_map": {
        {%- for variant in product.variants -%}
          "{{ variant.id }}": {{ variant.metafields.pixfizz.product_sku | default: product.metafields.pixfizz.product_sku | json }}
        {% unless forloop.last %},{% endunless %}{%- endfor -%}
      },
      "pixfizz_addons_map": {
        {%- for variant in product.variants -%}
          "{{ variant.id }}": {
            "page_addon": {{ variant.metafields.pixfizz.page_addon_product.value.id | default: product.metafields.pixfizz.page_addon_product.value.id | json }},
            "option_addons": {
              {%- for addon in product.metafields.pixfizz.option_addon_products.value -%}
                {{ addon.metafields.pixfizz.option_type_code | json }}: {
                  {%- for addon_variant in addon.variants -%}
                    {{ addon_variant.metafields.pixfizz.option_value_code | json }}: {{ addon_variant.id | json }}
                  {% capture variant_addons %}
                  {%- for addon in variant.metafields.pixfizz.option_addon_products.value -%}
                    {%- for addon_variant in addon.variants -%}
                      {{ addon_variant.metafields.pixfizz.option_value_code | json }}: {{ addon_variant.id | json }}
                    {% unless forloop.last %},{% endunless %}{%- endfor -%}
                  {%- endfor -%}
                  {% endcapture %}
                  {% assign variant_addons_stripped = variant_addons | strip %}
                  {% unless forloop.last and variant_addons_stripped == blank %},{% endunless %}{%- endfor -%}
                  {{ variant_addons }}
                }{% unless forloop.last %},{% endunless %}
              {%- endfor -%}
            }
          }{% unless forloop.last %},{% endunless %}
        {%- endfor -%}
      }
    }{% unless forloop.last %},{% endunless %}
  {% endfor %}
{% endpaginate %}
}

Create a Snippet

Create a snippet named pixfizz-saved-projects.liquid snippet in Shopify. Add the following code. The snippet was created to work with the Dawn theme and might have to be adapted to look good on other themes!

<div style="margin-top:2em;">
  <div>
    <h2>Saved Projects</h2>

    <table role="table" class="order-history">
      <caption class="visually-hidden">
        Saved Projects
      </caption>
      <thead role="rowgroup">
        <tr role="row">
          <th scope="col" role="columnheader">Preview</th>
          <th scope="col" role="columnheader">Updated</th>
          <th scope="col" role="columnheader">Name</th>
          <th scope="col" role="columnheader">Product</th>
        </tr>
      </thead>
      <tbody role="rowgroup">
        <template id="project-row">
          <tr role="row" class="project-item">
            <td role="cell" class="project-preview">
              <img src="" alt="" style="height:80px; display:block; cursor:pointer;" />
            </td>
            <td role="cell" class="project-updated">
            </td>
            <td role="cell" class="project-name">
            </td>
            <td role="cell" class="project-product">
            </td>
          </tr>
        </template>
      </tbody>
    </table>

    <div id="projects-loading" style="margin:1em 0; text-align:center;">
      <progress />
    </div>

    <div id="projects-load-more" style="margin:1em 0; text-align:center;" hidden>
      <a href="#">
        Load more&hellip;
      </a>
    </div>
  </div>
</div>

<script>
  const PROJECTS_PER_PAGE = 20;

  Pixfizz.Shopify.product_data_loader.load();
  
  const loaderDiv = document.querySelector('#projects-loading');
  const loadMoreDiv = document.querySelector('#projects-load-more');
  const projectRowTemplate = document.querySelector('#project-row');

  const loadProjects = (page) => {
    loaderDiv.hidden = false;
    loadMoreDiv.hidden = true;

    Pixfizz.Shopify.getSavedProjects(books => {
      books.forEach(book => {
        const product_id = book.options.shopify_product_id;
        const variant_id = book.options.shopify_variant_id;
  
        const date_formatter = new Intl.DateTimeFormat("en-us", {
          year: "numeric",
          month: "long",
          day: "numeric"
        });
        const date_parts = date_formatter.formatToParts(new Date(book.updated_at));
        const month = date_parts.find(p => p.type=== 'month').value;
        const day = date_parts.find(p => p.type === 'day').value;
        const year = date_parts.find(p => p.type === 'year').value;
        
        const tpl = projectRowTemplate.content.querySelector('tr');
        tpl.setAttribute('data-project-id', book.id);
        tpl.setAttribute('data-product-id', product_id);
        tpl.setAttribute('data-variant-id', variant_id);
        tpl.querySelector('.project-updated').innerText = `${month} ${day}, ${year}`;
        tpl.querySelector('.project-name').innerText = book.name;
  
        const node = tpl.cloneNode(true);
        projectRowTemplate.parentElement.appendChild(node);

        Pixfizz.Shopify.product_data_loader.get(product_id).then(data => {
          node.querySelector('.project-product').innerText = data ? data.title : '';
        });
        Pixfizz.Shopify.replaceImageWithProjectPreview(node.querySelector('.project-preview img'), book.id);
        
        node.querySelector('.project-preview img').addEventListener('click', evt => {
          evt.preventDefault();
          const item = evt.target.closest('.project-item');
          Pixfizz.Shopify.launchSavedProject(
            item.getAttribute('data-project-id'),
            item.getAttribute('data-product-id'),
            item.getAttribute('data-variant-id')
          );
        });
      });

      loaderDiv.hidden = true;
      if (books.length >= PROJECTS_PER_PAGE) {
        loadMoreDiv.hidden = false;
      }
    }, {page: page});
  };

  loadProjects(1);

  loadMoreDiv.addEventListener('click', evt => {
    evt.preventDefault();
    loadProjects(1 + Math.floor(document.querySelectorAll('.project-item').length / PROJECTS_PER_PAGE));
  })
</script>

Add the snippet to the Shopify account page

Locate the Shopify template/section that renders the default account page. For Dawn it’s the main-account.liquid section. Plug the snippet into the most appropriate place (see below code showing an example of Dawn theme)

{{ 'customer.css' | asset_url | stylesheet_tag }}

{%- style -%}
  .section-{{ section.id }}-padding {
    padding-top: {{ section.settings.padding_top | times: 0.75 | round: 0 }}px;
    padding-bottom: {{ section.settings.padding_bottom | times: 0.75 | round: 0 }}px;
  }

  @media screen and (min-width: 750px) {
    .section-{{ section.id }}-padding {
      padding-top: {{ section.settings.padding_top }}px;
      padding-bottom: {{ section.settings.padding_bottom }}px;
    }
  }
{%- endstyle -%}

<div class="customer account section-{{ section.id }}-padding">
  <div>
    <h1 class="customer__title">{{ 'customer.account.title' | t }}</h1>
    <a href="{{ routes.account_logout_url }}">
      <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" fill="none" viewBox="0 0 18 19">
        <path fill-rule="evenodd" clip-rule="evenodd" d="M6 4.5a3 3 0 116 0 3 3 0 01-6 0zm3-4a4 4 0 100 8 4 4 0 000-8zm5.58 12.15c1.12.82 1.83 2.24 1.91 4.85H1.51c.08-2.6.79-4.03 1.9-4.85C4.66 11.75 6.5 11.5 9 11.5s4.35.26 5.58 1.15zM9 10.5c-2.5 0-4.65.24-6.17 1.35C1.27 12.98.5 14.93.5 18v.5h17V18c0-3.07-.77-5.02-2.33-6.15-1.52-1.1-3.67-1.35-6.17-1.35z" fill="currentColor">
      </svg>
      {{ 'customer.log_out' | t }}
    </a>
  </div>

  <div>
    <div>
      <h2>{{ 'customer.orders.title' | t }}</h2>

      {% paginate customer.orders by 20 %}
        {%- if customer.orders.size > 0 -%}
          <table role="table" class="order-history">
            <caption class="visually-hidden">
              {{ 'customer.orders.title' | t }}
            </caption>
            <thead role="rowgroup">
              <tr role="row">
                <th id="ColumnOrder" scope="col" role="columnheader">{{ 'customer.orders.order_number' | t }}</th>
                <th id="ColumnDate" scope="col" role="columnheader">{{ 'customer.orders.date' | t }}</th>
                <th id="ColumnPayment" scope="col" role="columnheader">{{ 'customer.orders.payment_status' | t }}</th>
                <th id="ColumnFulfillment" scope="col" role="columnheader">
                  {{ 'customer.orders.fulfillment_status' | t }}
                </th>
                <th id="ColumnTotal" scope="col" role="columnheader">{{ 'customer.orders.total' | t }}</th>
              </tr>
            </thead>
            <tbody role="rowgroup">
              {%- for order in customer.orders -%}
                <tr role="row">
                  <td
                    id="RowOrder"
                    role="cell"
                    headers="ColumnOrder"
                    data-label="{{ 'customer.orders.order_number' | t }}"
                  >
                    <a
                      href="{{ order.customer_url }}"
                      aria-label="{{ 'customer.orders.order_number_link' | t: number: order.name }}"
                    >
                      {{ order.name }}
                    </a>
                  </td>
                  <td headers="RowOrder ColumnDate" role="cell" data-label="{{ 'customer.orders.date' | t }}">
                    {{ order.created_at | time_tag: format: 'date' }}
                  </td>
                  <td
                    headers="RowOrder ColumnPayment"
                    role="cell"
                    data-label="{{ 'customer.orders.payment_status' | t }}"
                  >
                    {{ order.financial_status_label }}
                  </td>
                  <td
                    headers="RowOrder ColumnFulfillment"
                    role="cell"
                    data-label="{{ 'customer.orders.fulfillment_status' | t }}"
                  >
                    {{ order.fulfillment_status_label }}
                  </td>
                  <td headers="RowOrder ColumnTotal" role="cell" data-label="{{ 'customer.orders.total' | t }}">
                    {{ order.total_net_amount | money_with_currency }}
                  </td>
                </tr>
              {%- endfor -%}
            </tbody>
          </table>
        {%- else -%}
          <p>{{ 'customer.orders.none' | t }}</p>
        {%- endif -%}

        {%- if paginate.pages > 1 -%}
          {%- if paginate.parts.size > 0 -%}
            <nav class="pagination" role="navigation" aria-label="{{ 'general.pagination.label' | t }}">
              <ul role="list">
                {%- if paginate.previous -%}
                  <li>
                    <a href="{{ paginate.previous.url }}" aria-label="{{ 'general.pagination.previous' | t }}">
                      <svg aria-hidden="true" focusable="false" viewBox="0 0 10 6">
                        <path fill-rule="evenodd" clip-rule="evenodd" d="M9.354.646a.5.5 0 00-.708 0L5 4.293 1.354.646a.5.5 0 00-.708.708l4 4a.5.5 0 00.708 0l4-4a.5.5 0 000-.708z" fill="currentColor">
                      </svg>
                    </a>
                  </li>
                {%- endif -%}

                {%- for part in paginate.parts -%}
                  <li>
                    {%- if part.is_link -%}
                      <a href="{{ part.url }}" aria-label="{{ 'general.pagination.page' | t: number: part.title }}">
                        {{- part.title -}}
                      </a>
                    {%- else -%}
                      {%- if part.title == paginate.current_page -%}
                        <span aria-current="page" aria-label="{{ 'general.pagination.page' | t: number: part.title }}">
                          {{- part.title -}}
                        </span>
                      {%- else -%}
                        <span>{{ part.title }}</span>
                      {%- endif -%}
                    {%- endif -%}
                  </li>
                {%- endfor -%}

                {%- if paginate.next -%}
                  <li>
                    <a href="{{ paginate.next.url }}" aria-label="{{ 'general.pagination.next' | t }}">
                      <svg aria-hidden="true" focusable="false" viewBox="0 0 10 6">
                        <path fill-rule="evenodd" clip-rule="evenodd" d="M9.354.646a.5.5 0 00-.708 0L5 4.293 1.354.646a.5.5 0 00-.708.708l4 4a.5.5 0 00.708 0l4-4a.5.5 0 000-.708z" fill="currentColor">
                      </svg>
                    </a>
                  </li>
                {%- endif -%}
              </ul>
            </nav>
          {%- endif -%}
        {%- endif -%}
      {% endpaginate %}

      {% render 'pixfizz-saved-projects' %}
    </div>

    <div>
      <h2>{{ 'customer.account.details' | t }}</h2>

      {{ customer.default_address | format_address }}

      <a href="{{ routes.account_addresses_url }}">
        {{ 'customer.account.view_addresses' | t }} ({{ customer.addresses_count }})
      </a>
    </div>
  </div>
</div>

{% schema %}
{
  "name": "t:sections.main-account.name",
  "settings": [
    {
      "type": "header",
      "content": "t:sections.all.padding.section_padding_heading"
    },
    {
      "type": "range",
      "id": "padding_top",
      "min": 0,
      "max": 100,
      "step": 4,
      "unit": "px",
      "label": "t:sections.all.padding.padding_top",
      "default": 36
    },
    {
      "type": "range",
      "id": "padding_bottom",
      "min": 0,
      "max": 100,
      "step": 4,
      "unit": "px",
      "label": "t:sections.all.padding.padding_bottom",
      "default": 36
    }
  ]
}
{% endschema %}

Create a Shopify Page

Create a new Page in the Shopify CMS (under “Online Store → Pages”). Give it the title “Pixfizz Product API” and select the “pixfizz-proudct-api” theme template for it.

Enable the save button in Editor

Go to your Pixfizz account and select "Settings" - "Design Tool"

Go to editor configuration and set the “Account Page Name” setting to “../account”. This will make the save button in shopify mode show up and direct the user to Shopify’s default /account page.

NOTE: We recommend incorporating the help guide to advise customers on how they can save their projects

PreviousShopify Installation - Variable PagesNextLinking products to shopify

Last updated 1 year ago

🛠️