Hotfix release available: 2025-05-14b "Librarian". upgrade now! [56.2] (what's this?)
Hotfix release available: 2025-05-14a "Librarian". upgrade now! [56.1] (what's this?)
New release available: 2025-05-14 "Librarian". upgrade now! [56] (what's this?)
Hotfix release available: 2024-02-06b "Kaos". upgrade now! [55.2] (what's this?)
Hotfix release available: 2024-02-06a "Kaos". upgrade now! [55.1] (what's this?)
New release available: 2024-02-06 "Kaos". upgrade now! [55] (what's this?)
Hotfix release available: 2023-04-04b "Jack Jackrum". upgrade now! [54.2] (what's this?)
Hotfix release available: 2023-04-04a "Jack Jackrum". upgrade now! [54.1] (what's this?)
New release available: 2023-04-04 "Jack Jackrum". upgrade now! [54] (what's this?)
Hotfix release available: 2022-07-31b "Igor". upgrade now! [53.1] (what's this?)
Hotfix release available: 2022-07-31a "Igor". upgrade now! [53] (what's this?)
New release available: 2022-07-31 "Igor". upgrade now! [52.2] (what's this?)
New release candidate 2 available: rc2022-06-26 "Igor". upgrade now! [52.1] (what's this?)
New release candidate available: 2022-06-26 "Igor". upgrade now! [52] (what's this?)
Hotfix release available: 2020-07-29a "Hogfather". upgrade now! [51.4] (what's this?)
New release available: 2020-07-29 "Hogfather". upgrade now! [51.3] (what's this?)
New release candidate 3 available: 2020-06-09 "Hogfather". upgrade now! [51.2] (what's this?)
New release candidate 2 available: 2020-06-01 "Hogfather". upgrade now! [51.1] (what's this?)
New release candidate available: 2020-06-01 "Hogfather". upgrade now! [51] (what's this?)
Hotfix release available: 2018-04-22c "Greebo". upgrade now! [50.3] (what's this?)
Hotfix release available: 2018-04-22b "Greebo". upgrade now! [50.2] (what's this?)
single_page_catalog

Single Page Catalog

Objective

The single page catalog seeks to provide one unified page on which:

  • Users see My Learning items, Required/Recommended items, browse and search the general catalog items, register/pay for courses and immediately play them in one single page.
  • Provide a responsive UI
  • User can choose between “Single Page” and “Classic” UI(current panel design)

Functionality

Complete details of the single page catalog functionalities are provided in the following link:

https://wiki.exphosted.com/doku.php/single_page_catalog_functionality

Implementation

1) Create a controller action which renders single page view, once the page is rendered every request after that will is an Ajax call.

2) Need to create a layout named “single_page” which will load Vuejs framework, bootstrap css and other vue compoonents.

3) There can be 2 approaches to fetch data from Learnexa

  • Making use of existing controllers to support json response
  • Creating an API support and fetching data based on authenticity token passed on each API call

4) On using existing controllers we need not have to make much code changes, all data fetch code is already available only json response need to be added(recommended option). Just an Ajax call will retrieve all the expected data.

5) On using API call session management should be handled and authentication token should be saved in session to make use of it for each API call. All this session management/token authentication has to be implemented(implementation time will increase).

6) On using either of the above approach, view rendering is achieved using Vuejs framework. Every view part of the page will be developed as a component.

7) All Ajax request from UI is achieved using Vue-resource, the plugin for Vue.js provides services for making web requests and handle responses using a XMLHttpRequest or JSONP.

8) Responsive and design styles are achieved using Bootstrap js

9) Following are the bootstrap classes that can be used to achieve the expected UI

  • modal-popup can be used to create a responsive light box.
  • .img-rounded/.img-thumbnail can be used for the course thumbnail img
  • .btn-link for all the blue links in site
  • .badge to attach the number count to image links(notification/inbox/cart)
  • .breadcrumb to align all link in horizontal line
  • .dropdown, .dropdown-menu can be used for all dropdowns
  • .collapse can be used for more item button in product details page
  • .buttonload for loading icon/button
  • .modal for confirmation/alert popups
  • Code for banner image
      body, html {
       height: 100%;
       margin: 0;
      }

      .bg {
        /* The image used */
        background-image: url("img_girl.jpg");
        height: 50%; 

       /* Center and scale the image nicely */
       background-position: center;
       background-repeat: no-repeat;
       background-size: cover;
      }
      

10) The user should be able to switch between the old “Classic” UI(current panel design) and the “Single Page” UI by making a setting change in the “Site Properties” page.

DB Changes

Table name - companies

Column_name type Description
single_page_catalog_enabled boolean default false

Mocks

POC Using Existing Controllers

Create a sample page which will make an ajax call to existing controllers, fetch products and dynamically render using Vue component:

1) HTML file looks like:

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
  <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
  <link rel="stylesheet" href="bootstrap.min.css">
  <script src="vue.js"></script>
  <script src="vue-resource.js"></script>
  <script src="javascripts/jquery.tools.min.js"></script>
  <script src="vue_template.js"></script>
  <script>
    jQuery(document).ready(function() {
      new Vue({
          el: '#example',
	  data: {
	     items: []
	  },
	  mounted: function() {
	    this.$http.get('/products.json').then(response => {
              this.items = response.body;
            }, response => { });
          }
      });
    });
  </script>
  <style>
    .thumbnail {
      box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
    }
    .title {
      margin-left: 20px;
      text-align: left;
    }
  </style>
</head>
<body>
  <div class="bg-1 text-center">
    <img src="test1.png" class="img-responsive" style="display:inline" alt="Bird" width="100%" height="100">
    <h3 class="title">Available Courses</h3>
  </div>
  <div id="products">
    <div id="example">
      <my-component v-for="item in items" :item="item"></my-component>
    </div>
  </div>
</body>
</html>

2) Sample products controller app/controllers/products_controller.rb (already existing controller)

class ProductsController < BaseController

  def index
    search_featured_items = Product.search(current_account, current_user, params.merge(:only_featured => true))
    search_except_featured_items = Product.search(current_account, current_user, params.merge(:except_featured => true))
    @products = search_featured_items.results + search_except_featured_items.results
    
    respond_to do |format|
      format.json  { render :json => @products.to_json }
    end
  end

end

3) Vue template for rending item tile structure comes from vue_template.js

Vue.component('my-component', {
   template: `<div class="col-md-3 z-depth-2">
	        <div class="thumbnail">
		  <a :href="item.product.thumbnail_src_url">
	            <img :src="item.product.thumbnail_src_url" alt="Lights" style="width:100%" ref="lazyLoadingImage">
		    <div class="caption">
	              <p> <strong> {{item.product.title}} </strong> </p>
		      <p v-html="item.product.description"></p>
		    </div>
		  </a>
		</div>
	      </div>`,
    props: {
      item: Object,
    }
})

POC For API Approach

Create a sample page which will make an API call to fetch products and dynamically render using Vue component:

1) HTML file looks like:

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
  <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
  <link rel="stylesheet" href="bootstrap.min.css">
  <script src="vue.js"></script>
  <script src="vue-resource.js"></script>
  <script src="javascripts/jquery.tools.min.js"></script>
  <script src="vue_template.js"></script>
  
  <script>
    jQuery(document).ready(function() {
      new Vue({
          el: '#example',
	  data: {
	     items: []
	  },
	  mounted: function() {
	    this.$http.get('http://localhost:3000/api/v1/products.json?access_token=<token>&company_id=<id>&user_id=<id>').then(response => {
              this.items = response.body;
            }, response => { });
          }
      });
    });
  </script>
  <style>
    .thumbnail {
      box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
    }
    .title {
      margin-left: 20px;
      text-align: left;
    }
  </style>
</head>
<body>
  <div class="bg-1 text-center">
    <img src="test1.png" class="img-responsive" style="display:inline" alt="Bird" width="100%" height="100">
    <h3 class="title">Available Courses</h3>
  </div>
  <div id="products">
    <div id="example">
      <my-component v-for="item in items" :item="item"></my-component>
    </div>
  </div>
</body>
</html>

2) Enable API in application by adding following in 'config/application.yml'

 api_enabled: true

3) Created sample api controller app/controllers/api/v1/products_controller.rb

class Api::V1::ProductsController < Api::V1::BaseController
  before_filter :verify_access_token

  def index
    current_account = Company.find_by_id(params[:company_id])
    current_user = User.find_by_id(params[:user_id])
    search_featured_items = Product.search(current_account, current_user, params.merge(:only_featured => true))
    search_except_featured_items = Product.search(current_account, current_user, params.merge(:except_featured => true))
    @products = search_featured_items.results + search_except_featured_items.results
  end

end

4) API render file is index.rabl, show.rabl

index.rabl:
collection @products

extends "api/v1/products/show"

show.rabl:
object @product
attributes :id, :title, :description, :item_type, :item_id, :thumbnail_url, :description

child :categories => :categories do
  attributes :id, :name
end

child :tags => :tags do
  attributes :id, :name
end

5) Vue template for rending item tile structure comes from vue_template.js

Vue.component('my-component', {
   template: `<div class="col-md-3 z-depth-2">
	        <div class="thumbnail">
		  <a :href="item.product.thumbnail_src_url">
	            <img :src="item.product.thumbnail_src_url" alt="Lights" style="width:100%" ref="lazyLoadingImage">
		    <div class="caption">
	              <p> <strong> {{item.product.title}} </strong> </p>
		      <p v-html="item.product.description"></p>
		    </div>
		  </a>
		</div>
	      </div>`,
    props: {
      item: Object,
    }
})

6) Screen shot of the sample page:

Code Snippets for Animation

1) Play progress button code using progressbar.js plugin:

<script src="progressbar.js"></script>

<div id="container">
  <img src="play.png">
  <span> 30% </span>
</div>

<script>
  var bar = new ProgressBar.Circle(container, {
  easing: 'easeInOut',
  duration: 400,
  color: '#ffffff',
  trailColor: '#f2958e',
  strokeWidth: 10,
  trailWidth: 10,
  svgStyle: null
});

bar.animate(0.3);
</script>
<style>
   #container img {
    position: absolute;
    left: 29px;
    top: 21px;
   }
   #container {
    margin: 20px;
    width: 68px;
    height: 68px;
   }
   #container span {
    position: absolute;
    top: 67px;
    font-size: 12px;
    color: #fff;
    left: 52px;
   }
</style>

single_page_catalog.txt · Last modified: 2018/08/31 16:16 (external edit)