Implementing Tickets API on Open Event Frontend to Display Tickets

This blog article will illustrate how the tickets are displayed on the public event page in Open Event Frontend, using the tickets API. It will also illustrate the use of the add on, ember-data-has-query, and what role it will play in fetching data from various APIs. Our discussion primarily will involve the public/index route. The primary end point of Open Event API with which we are concerned with for fetching tickets for an event is

GET /v1/events/{event_identifier}/tickets

Since there are multiple  routes under public  including public/index, and they share some common event data, it is efficient to make the call for Event on the public route, rather than repeating it for each sub route, so the model for public route is:

model(params) {
return this.store.findRecord('event', params.event_id, { include: 'social-links' });
}

This modal takes care of fetching all the event data, but as we can see, the tickets are not included in the include parameter. The primary reason for this is the fact that the tickets data is not required on each of the public routes, rather it is required for the index route only. However the tickets have a has-many relationship to events, and it is not possible to make a call for them without calling in the entire event data again. This is where a really useful addon, ember-data-has-many-query comes in.

To quote the official documentation,

Ember Data‘s DS.Store supports querying top-level records using the query function.However, DS.hasMany and DS.belongsTo cannot be queried in the same way.This addon provides a way to query has-many and belongs-to relationships

So we can now proceed with the model for public/index route.

model() {
const eventDetails = this._super(...arguments);
return RSVP.hash({
  event   : eventDetails,
  tickets : eventDetails.query('tickets', {
    filter: [
      {
        and: [
          {
            name : 'sales-starts-at',
            op   : 'le',
            val  : moment().toISOString()
          },
          {
            name : 'sales-ends-at',
            op   : 'ge',
            val  : moment().toISOString()
          }
        ]
      }
    ]
  }),

We make use of this._super(…arguments) to use the event data fetched in the model of public route, eliminating the need for a separate API call for the same. Next, the ember-has-many-query add on allows us to query the tickets of the event, and we apply the filters restricting the tickets to only those, whose sale is live.
After the tickets are fetched they are passed onto the ticket list component to display them. We also need to take care of the cases, where there might be no tickets in case the event organiser is using an external ticket URL for ticketing, which can be easily handled via the is-ticketing-enabled property of events. And in case they are not enabled we don’t render the ticket-list component rather a button linked to the external ticket URL is rendered.  In case where ticketing is enabled the various properties which need to be computed such as the total price of tickets based on user input are handled by the ticket-list component itself.

{{#if model.event.isTicketingEnabled}}
  {{public/ticket-list tickets=model.tickets}}
{{else}}
<div class="ui grid">
  <div class="ui row">
      <a href="{{ticketUrl}}" class="ui right labeled blue icon button">
        <i class="ticket icon"></i>
        {{t 'Order tickets'}}
      </a>
  </div>
  <div class="ui row muted text">
      {{t 'You will be taken to '}} {{ticketUrl}} {{t ' to complete the purchase of tickets'}}
  </div>
</div>
{{/if}}

This is the most efficient way to fetch tickets, and also ensures that only the relevant data is passed to the concerned ticket-list component, without making any extra API calls, and it is made possible by the ember-data-has-many-query add on, with very minor changes required in the adapter and the event model. All that is required to do is make the adapter and the event model extend the RestAdapterMixin and ModelMixin provided by the add on, respectively.

Resources

Create Discount Code Component in Open-Event-Frontend

We in Open-Event-Frontend have given the event organiser the feature to create discount coupons for his or her event. Here the organiser can either enter the discount amount or discount percentage and can set even set the total number of coupons he wants to make available for his customers. We have also automatically generated an unique link for each discount coupon.

We’ll be creating a separate component create-discount-code for creating discount codes.To create the component we’ll run the following command

ember g component forms/events/view/create-discount-code

This will create

1.Create-discount-code.hbs

Here we have designed our form.We have nested all the fields used, inside semantic’s ui form class.Some of the helpers used in the form are

We have used the ember input helper in following way for all the input fields.The

attribute name,value corresponds to the id and value attached with the helper

{{input type=‘text’ name=‘discount_code’ value=data.code}}

Ember radio buttons are used by the organizer to select between discount

{{ui-radio label=(t ‘Amount (US$)’)
          name=‘discount_type’  
          value=‘amount’
          current=‘amount’
          onChange=(action (mut selectedMode))}}

 

We have given the organizer an additional option to set the validity of the discount code. For this we have used date-picker and time-picker component already present in Open-Event-Frontend in the following manner.

<div class=“fields”>
       <div class=“wide field {{if device.isMobile ‘sixteen’ ‘five’}}”>
         <label>{{t ‘Valid from’}}</label>
         {{widgets/forms/date-picker id=’start_date’ value=data.validFromDate rangePosition=’start’}}
         <div class=“ui hidden divider”></div>
         {{widgets/forms/time-picker id=’start_time’ value=data.validFromTime rangePosition=’start’}}
       </div>
       <div class=“wide field {{if device.isMobile ‘sixteen’ ‘five’}}”>
         <label>{{t ‘Expires on’}}</label>
         {{widgets/forms/date-picker id=‘end_date’ value=data.validTillDate rangePosition=‘end’}}
         <div class=“ui hidden divider”></div>
         {{widgets/forms/time-picker id=‘end_time’ value=data.validTillTime rangePosition=‘end’}}
       </div>
     </div>

The above snippet will the following output

2.Create-discount-code.js

Here we validate the form and provide it with an unique discount code url. We have generated the url using the event id and the discount code.

discountLink: computed(‘data.code’, function() {
 const params = this.get(‘routing.router.router.state.params’);
 return location.origin + this.get(‘routing.router’)
                         .generate(‘public’, params[‘events.view’]                          .event_id,
        { queryParams: { discount_code: this.get(‘data.code’) } });
}),
actions: {
 submit() {
   this.onValid(() => {
   });
 }
}

3.Create-discount-code-test.js

This is where we check whether our component is compatible with other components of the system or not. Here, for now, we are just making sure if our component renders or not, by checking the presence of ‘Save’.

import { test } from ’ember-qunit’;
import moduleForComponent from ‘open-event-frontend/tests/helpers/component-helper’;
import hbs from ‘htmlbars-inline-precompile’;

moduleForComponent(‘forms/events/view/create-discount-code’, ‘Integration | Component | forms/events/view/create discount code’);

test(‘it renders’, function(assert) {
 this.render(hbs`{{forms/events/view/create-discount-code routing=routing}}`);
 assert.ok(this.$().html().trim().includes(‘Save’));
});

Now, our component is ready, and the only part remaining is to place it in our application. We place it in app/templates/events/view/tickets/discount-codes/create.hbs in the given form.

{{forms/events/view/create-discount-code data=model}}

Here we have passed model from create-discount-code.js to data used in Create-discount-code.hbs

Now our create discount code page is up and running

Additional Resources