Semantic-UI Validations for Forms in Open Event Frontend

Open Event Frontend requires forms at several places like at the time of login, for creation of events, taking the details of the user, creating discount codes for tickets etc.. Validations for these forms is a must, like in the above picture, we can see that many fields like discount code, amount etc. have been left empty, these null values when stored at backend can induce errors.

Semantic-UI makes our life easier and provides us with it’s own validations. Its form validation behavior checks data against a set of criteria or rules before passing it along to the server.

Let’s now dive deeper into Semantic validations, we’ll take discount code form as our example. The discount code form has many input fields and we have put checks at all of them, these checks are called rules here. We’ll discuss all the rules used in this form one by one

  1. Empty

Here we check if the input box with the identifier discount_amount is empty or not, if it is empty, a prompt is shown with the given message.

         identifier : ‘discount_amount’,
         rules      : [
           {
             type   : ’empty’,
             prompt : this.l10n.t(‘Please enter the discount amount’)
           }
         ]

2. Checked
Here, we validate whether the checkbox is checked or not and if it is not, show corresponding message

rules      : [
   {
     type   : ‘checked’,
     prompt : this.l10n.t(‘Please select the appropriate choices’)
   }]

3. RegExp

These checks are very important in input fields requiring passwords and codes, they specify the allowed input characters

rules      : [{
   type  : ‘regExp’,
   value : ‘^[a-zA-Z0-9_-]*$’
}]

4.Custom rules

Many a times, we require some rules which are by default not given by semantic, here we can create custom rules.

Like here, we want to check whether the user has not set max value lower than min.

$.fn.form.settings.rules.checkMaxMin = () => {
     if (this.get(‘data.minQuantity’) > this.get(‘data.maxQuantity’)) {
       return false;
     }
     return true;
   };

Here, we are creating our own custom rule checkMaxMin which returns boolean value depending upon minQuantity and maxQuantity. Now, this can be directly used as a rule

identifier : ‘min_order’,
optional   : true,
rules      : [
 {
  type   : ‘checkMaxMin’,
  prompt : this.l10n.t(‘Minimum value should not be greater than maximum’)
 }]

You can find the above code here

Additional Resources

Dynamic Segments in Open Event Frontend

In the Open Event Frontend project we have a page where we show all types of events. We have classified events into six classes like live, draft, past etc. Now each event type should have it’s own page describing it. What should we do now? Make six different routes corresponding to each class of event? Isn’t that too cumbersome. Is there any other method to do it?

Dynamic segment is the answer to the above question. Dynamic segment is that segment of the path for a route that will change based on content of  the page. Dynamic segments are frequently used in Open Event Frontend.

One such use is in /admin/events. Here we have button for different categories of events. We do not make separate routes for each of them, instead we use dynamic segments. We’ll have a single route and we will change the data in the route corresponding to the tab chosen.

Lets now add dynamic segments to /admin/events. First of all we’ll add the following code snippet in router.js.

this.route(‘events’, function() {
     this.route(‘list’, { path: ‘/:events_status’ });
     this.route(‘import’);
   });

Here : signifies the presence of dynamic segment and it is followed by an identifier. It’s the identifier by which the route matches the corresponding model to show.

Now as our route would show data depending upon the tab selected, we must change the title of page depending upon the same. For this we add the following code snippet in admin/events/list.js. Here we set the title of the page using titleToken() function and access the dynamic portion of url using params.events_status

export default Route.extend({
 titleToken() {
   switch (this.get(‘params.events_status’)) {
     case ‘live’:
       return this.l10n.t(‘Live’);
     case ‘draft’:
       return this.l10n.t(‘Draft’);
     case ‘past’:
       return this.l10n.t(‘Past’);
     case ‘deleted’:
       return this.l10n.t(‘Deleted’);
   }
 },
 model(params) {
   this.set(‘params’, params);
   return [{
   // Events data
   }];
 }
});

We’ll now link the tabs template/admin/events to the corresponding models using Link-to the helper.Here we have linked ‘Live’, ‘Draft’, ‘Past’, ‘Deleted’ buttons to dynamic segments. Let’s understand how it works.Let’s take example of Live button.Live button is linked to admin/events/list and this list is replaced by ‘live’. So our final route becomes admin/events/live.

<div class=“ui grid stackable”>
 <div class=“row”>
   <div class=“sixteen wide column”>
     {{#tabbed-navigation isNonPointing=true}}
       {{#link-to ‘admin.events.index’ class=’item’}}
         {{t ‘All Events’}}
       {{/link-to}}
       {{#link-to ‘admin.events.list’ ‘live’ class=’item’}}
         {{t ‘All Live’}}
       {{/link-to}}
       {{#link-to ‘admin.events.list’ ‘draft’ class=’item’}}
         {{t ‘All Draft’}}
       {{/link-to}}
       {{#link-to ‘admin.events.list’ ‘past’ class=’item’}}
         {{t ‘All Past’}}
       {{/link-to}}
       {{#link-to ‘admin.events.list’ ‘deleted’ class=’item’}}
         {{t ‘All Deleted’}}
       {{/link-to}}
       {{#link-to ‘admin.events.import’ class=’item’}}
         {{t ‘Import’}}
       {{/link-to}}
     {{/tabbed-navigation}}
   </div>
 </div>
 <div class=“row”>
   {{outlet}}
 </div>
</div>

Additional 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

Adding Messaging Route in Ember.js for Admin UI of Open Event Frontend

In this blog post I am explaining how we implement a messages page for admins to keep track of all types of system messages sent to users in the Open Event Frontend. The page shows the types of messages sent out to various users at one place and as well as additional details. It offers configuration options to control which messages get sent out  as emails or notifications or both. And, the page shows when and what message should be sent via notification or mail.
To create the messages page we’ll run the following command

ember generate route admin/messages

This will create

This command will also add  this.route(‘messages’);  to router.js. As admin is the parent route for messages, messages will be nested inside admin in router.js

this.route(‘admin’, function(){
  this.route(‘messages’);
});

Let’s now understand the content of each of above files.

  1. Messages.js

In admin/messages.js we have used titletoken helper to set the title of the tab. Here we have created the message model and added attribute like recipient, trigger, emailMessage, notificationMessage, options and sentAt. We have returned this model from the js file to template.

import Ember from ’ember’;
const { Route } = Ember;
export default Route.extend({
 titleToken() {
   return this.l10n.t(‘Messages’);
 },
 model() {
   return [{
     recipient: [
       {
         name: ‘Organizer’
       },
       {
         name: ‘Speaker’
       }
     ],
     trigger      : ‘Title1’,
     emailMessage : {
       subject : ‘Email subject1’,
       message : ‘Hi, the schedule for session1 has been changed’
     },
     notificationMessage: {
       subject : ‘Notification subject1’,
       message : ‘Hi, the schedule for session1 has been changed’
     },
     option: {
       mail         : true,
       notification : false,
       userControl  : true
     },
     sentAt: new Date()
   }];
 }
});

 

  1. Messages.hbs

In template we have created a table and added classes like stackable and compact. Stackable class makes the table responsive and stacks all the rows for devices with smaller screen size. Compact class helps to show more number of rows at a time.

Then in the template we iterate through the model using a loop. Here we have used other semantic-ui elements like ui ordered list , ui header, ui-checkbox inside the table. For options column we have three attributes denoting how the admin wants to send the message to the user. Here we have grouped three fields using the class grouped fields .In each field we have used semantic’s  ui-checkbox .In check-box we are mutating values on click by using mut helper.

<div class=“grouped fields”>
 <div class=“field”>
   {{ui-checkbox checked=message.option.mail
                 label=(t ‘Mail’)      
                 onChange=(action (mut message.option.mail))}}
 </div>
 <div class=“field”>
   {{ui-checkbox checked=message.option.notification
                 label=(t ‘Notification’)  
               onChange=(action (mut message.option.notification))}}
 </div>

 <div class=“field”>
   {{ui-checkbox checked=message.option.userControl
                label=(t ‘User Control’)  
               onChange=(action (mut message.option.userControl))}}
 </div>
</div>

We are getting date object from js and to convert it into human readable format we have used moment like {{moment-format message.sentAt ‘dddd, DD MMMM YYYY’}}

  1. Messages-test.js
import { test } from ’ember-qunit’;
import moduleFor from ‘open-event-frontend/tests/helpers/unit-helper’;

moduleFor(‘route:admin/messages’, ‘Unit | Route | admin/messages’, []);

test(‘it exists’, function(assert) {
 let route = this.subject();
 assert.ok(route);
});

Using this we can test the existence of our route. These tests are run using the command ember t.

Our message page is ready now. The admin can have a check at all the messages sent to users.

Additional Resources

Nested Routes in Ember JS in Open Event Frontend

When defining routes in ember, there is one very important thing to keep in mind. We should only nest our routes when our UI is nested. Sometimes it is necessary to display a template inside another template and here we use nested routes.

Let’s now learn how to create nested routes. We can take a look at the notifications page in our Open Event Frontend for better understanding.

Here the part in red is common to all the sub-routes of notification, thus one method of achieving this could be copy-pasting the same code in all the sub-routes. Obviously this is not the correct method and it increases the code redundancy.

The correct method here is, using nested routes.

ember generate route notifications
ember generate route notifications/index
ember generate route notifications/all

Here notification is the parent route and index, all are it’s nested or sub-routes. It can be seen in router.js in the given form

this.route(‘notifications’, function() {
   this.route(‘all’);
});

But wait we see no route for notifications/index…. did something go wrong ?

 

No. At every level of nesting, ember automatically creates a route for the path ‘/’ named index. So the above code snippet in router.js is equivalent to

this.route(‘notifications’, function() {

   this.route(‘index’,{ path: ‘/’});
   this.route(‘all’);
});

Now let’s have a look at our parent route ie /notifications. As stated above only the part in red has to be present here, So we have added semantic-ui’s header, buttons and used link-to helper to link them to respective route. An interesting thing to note here is that ‘Mark all read’ is not present in both its sub routes, so we need to check the route before displaying this button. We have checked the current route using session.currentRouteName and only when the current route is not notifications/all we display the button.

<h1 class=“ui header”>{{t ‘Notifications’}}</h1>
<div class=”ui divider”></div>
<div class=“row”>
 {{#link-to ‘notifications’}}
   <button class=“ui button”>{{t ‘Unread’}}</button>
 {{/link-to}}
 {{#link-to ‘notifications.all’}}
   <button class=“ui button”>{{t ‘All’}}</button>
 {{/link-to}}
 {{#if (not-includes session.currentRouteName ‘notifications.all’)}}
   <button class=“ui button right floated”>{{t ‘Mark all read’}}</button>
 {{/if}}
</div>
{{outlet}}

We have added the {{outlet}} helper in our notifications.hbs where we want our nested templates to be displayed. For understanding we can imagine this as the whole code of notifications/all is being copied into this outlet helper i.e notifications/all.hbs is rendered into the {{outlet}} of notifications.hbs

Let’s now have a look at our sub-route i.e notifications/all.hbs. We now know that this will be rendered in the red part stated below

We have added Semantic-Ui’s segments class for every notification. A blue colour has been added to the segments if the notification is not read. We have used Semantic-Ui’s grid class to structure the content inside each notifications. We are getting date object from js and to convert it into human readable format we have used moment like {{moment-from-now notification.createdAt}}

{{#each model as |notification|}}
 <div class=“ui segment {{unless notification.isRead ‘blue’}}”>
   <div class=“ui grid”>
     <div class=“row”>
       <div class=“eight wide column”>
         <h4 class=“ui header”>{{notification.title}}</h4>
         <p>{{notification.description}}</p>
       </div>
       <div class=“eight wide column right aligned”>
         <i class=“checkmark box icon”></i>
       </div>
     </div>
     <div class=“row”>
       <div class=“ten wide column”>
         <button class=“ui blue button”>{{t ‘View Session’}}</button>
       </div>
       <div class=“six wide column right aligned”>
         <p>{{moment-from-now notification.createdAt}}</p>
       </div>
     </div>
   </div>
 </div>
{{/each}}

We have reused notifications/all.hbs for index route by explicitly defining templateName: ‘notifications/all’, in notifications/index.js.

Additional Resources

Adding User Route in Ember.js to Admin UI of Open Event Frontend

The Users tab in Admin routes, lets the admin have a check on all the users who have ever used our website. We in Open-Event-Frontend have classified the users into active and deleted users. This way an admin has both present and past records.

To create the users page we’ll run the following command

ember generate route admin/users

This will create

This command will also add  this.route(‘users’);  to router.js

Now let’s understand the content of each of these files

  1. User.js

In admin/users.js we have used titletoken helper to set the title of the tab. Here we have created the user model and added attribute like name, email, status, systemRoles, eventRoles, createdAt and lastAccessedAt. We have returned this model from the js file to template.

import Ember from ’ember’;

const { Route } = Ember;

export default Route.extend({
 titleToken() {

     return this.l10n.t(‘User’);

 },
 model() {
   return [{
     name        : ‘Test name 1’,
     email       : [email protected],
     status      : ‘active’,
     systemRoles : [‘unverified user1’, ‘unverified user2’],
     eventRoles  : [
       {
         name  : ‘Organizer’,
         event : {
           name: ‘Event One’
         }
       },
       {
         name  : ‘Organizer’,
         event : {
           name: ‘Event Two’
         }
       }
     ],
     createdAt      : new Date(),
     lastAccessedAt : new Date()
   }];
 }
});

 

  1. User.hbs

In template we have created a table and added classes like stackable and compact. Stackable class makes the table responsive and stacks all the rows for devices with smaller screen size. Compact class helps to show more number of rows at a time.

Then in the template we iterate through the model using a loop. Here we have used other semantic-ui elements like ui ordered list , ui label, ui-popup inside the table. We are getting date object from js and to convert it into human readable format we have used moment like {{moment-from-now user.lastAccessedAt}}

 <table class=“ui compact stackable very basic table”>
   <thead>
     <tr>
       <th>{{t ‘Name’}}</th>
       <th>{{t ‘Email’}}</th>
       <th>{{t ‘Status’}}</th>
       <th>{{t ‘System Roles’}}</th>
       <th>{{t ‘Event Roles’}}</th>
       <th>{{t ‘User Links’}}</th>
       <th>{{t ‘Member Since’}}</th>
       <th>{{t ‘Last Access’}}</th>
       <th>{{t ‘Options’}}</th>
     </tr>
   </thead>
   <tbody>
     {{#each model as |user|}}
       <tr>
         <td>
           {{user.name}}
         </td>
         <td>
           {{user.email}}
         </td>
         <td>
           {{#if (eq user.status ‘active’)}}
             <div class=”ui green label”>{{t ‘Active’}}</div>
           {{else}}
             <div class=“ui red label”>{{t ‘Inactive’}}</div>
           {{/if}}
         </td>
         <td>
           <div class=”ui ordered list”>
             {{#each user.systemRoles as |systemRole|}}
               <div class=”item”>{{systemRole}}</div>
             {{/each}}
           </div>
         </td>
         <td>
           <div class=“ui ordered list”>
             {{#each user.eventRoles as |eventRole|}}
               <div class=“item”>{{eventRole.name}} ({{eventRole.event.name}})</div>
             {{/each}}
           </div>
         </td>
         <td>
           <div class=“ui ordered list”>
             <div class=“item”>
               <a href=‘#’ target=‘_blank’ rel=‘noopener’>{{t ‘Sessions’}}</a>
             </div>
             <div class=“item”>
               <a href=‘#’ target=‘_blank’ rel=‘noopener’>{{t ‘Events’}}</a>
             </div>
             <div class=“item”>
               <a href=‘#’ target=‘_blank’ rel=‘noopener’>{{t ‘Tickets’}}</a>
             </div>
             <div class=“item”>
               <a href=‘#’ target=‘_blank’ rel=‘noopener’>{{t ‘Settings’}}</a>
             </div>
           </div>
         </td>
         <td>
           {{moment-from-now user.createdAt}}
         </td>
         <td>
           {{moment-from-now user.lastAccessedAt}}
         </td>
         <td>
           <div class=“ui vertical compact basic buttons”>
             {{#ui-popup content=(t ‘View’) class=’ui icon button’ position=’left center’}}
               <i class=“unhide icon”></i>
             {{/ui-popup}}
             {{#ui-popup content=(t ‘Edit’) class=’ui icon button’ position=’left center’}}
               <i class=“edit icon”></i>
             {{/ui-popup}}
             {{#ui-popup content=(t ‘Delete’) class=’ui icon button’ position=’left center’}}
               <i class=“trash outline icon”></i>
             {{/ui-popup}}
           </div>
         </td>
       </tr>
     {{/each}}
   </tbody>
 </table>

 

  1. Users-test.js
ember timport { test } from ’ember-qunit’;
import moduleFor from ‘open-event-frontend/tests/helpers/unit-helper’;

moduleFor(‘route:admin/users’, ‘Unit | Route | admin/users’, []);

test(‘it exists’, function(assert) {
 let route = this.subject();
 assert.ok(route);
});

Here we can testing the existence of our route. These tests are run using the command ember test.

Our user page is ready now. You can see how the user’s information is available in a formatted manner on the page. We have achieved our goal, by using the above mentioned files.

Additional Resources

Implementing Responsive Designs in Open Event Front-end

Screen size differs from user to user, from device to device and thus we must provide responsive user interface so that our design doesn’t break on any of the devices. At Open-Event-Frontend we have used Semantic-UI elements to implement responsive designs. Semantic-UI has classes like grid, stackable, container etc to maintain the responsiveness of the designs. Semantic also gives us functions to check the size of the screen we are currently in and thus depending upon the screen size, we can design our user-interface.

Let’s take /events/<event_identifier>  for an example

Two components have been added to this page

  1. The following four buttons that we are using, are ‘ui buttons’ and have icons.
  1. Preview – This button allows us to check how our event would look before it is published.
  2. Publish – When we are ready we can make our event available for the world
  3. Copy – Using this we can copy all the details of our event and create a new event  from it
  4. Delete – If something went wrong we can delete our event at any time

From the above mentioned buttons we have grouped Publish and Copy together.

It is done to add responsive behaviour. In case of mobile ,our buttons only have icon and no text ,and buttons are no longer right aligned. For this we have used device.isMobile to check whether we are on mobile or not to ensure the responsive behaviour is carried over to mobile too.

<div class=“twelve wide column {{unless device.isMobile ‘right aligned’}}”>
       {{#if device.isMobile}}
         <div class=“ui icon fluid buttons”>
           <button class=“ui button”><i class=“unhide icon”></i></button>
           <button class=“ui button”><i class=“check icon”></i></button>
           <button class=“ui button”><i class=“copy icon”></i></button>
           <button class=“ui red button” {{action ‘openDeleteEventModal’}}><i class=“trash icon”></i></button>
         </div>
       {{else}}
           <button class=”ui button labeled icon small”>
             <i class=”unhide icon”></i>
             {{t ‘Preview’}}
           </button>
           <div class=”ui small labeled icon buttons”>
             <button class=”ui button “>
               <i class=”check icon”></i>
               {{t ‘Publish’}}
             </button>
             <button class=”ui button “>
               <i class=”copy icon”></i>
               {{t ‘Copy’}}
             </button>
           </div>
           <button class=“ui red button labeled icon small” {{action ‘openDeleteEventModal’}}>
             <i class=“trash icon”></i>
             {{t ‘Delete’}}
           </button>
       {{/if}}
     </div>

Here class `right aligned` is added dynamically depending upon whether we are viewing on mobile or not.

  1. We have added tabs for all types of elements of events like tickets, speakers, scheduler. Also, we have made these tabs responsive. For mobile these tabs converts to stackable menu.


Here also classes are added depending on some condition.

<div class=“ui fluid pointing stackable menu {{unless device.isMobile ‘secondary’}}”>

Conditional addition of classes is a very useful and powerful feature of handlebars.

<div class=“row” style=“padding-top: 15px”>
   <div class=“sixteen wide column”>
     <div class=“ui fluid pointing stackable menu {{unless device.isMobile ‘secondary’}}”>
       {{#link-to ‘events.view.index’ class=’item’}}
         {{t ‘Overview’}}
       {{/link-to}}
       {{#link-to ‘events.view.tickets’ class=’item’}}
         {{t ‘Tickets’}}
       {{/link-to}}
       <a href=“#” class=‘item’>{{t ‘Scheduler’}}</a>
       {{#link-to ‘events.view.sessions’ class=’item’}}
         {{t ‘Sessions’}}
       {{/link-to}}
       {{#link-to ‘events.view.speakers’ class=’item’}}
         {{t ‘Speakers’}}
       {{/link-to}}
       {{#link-to ‘events.view.export’ class=’item’}}
         {{t ‘Export’}}
       {{/link-to}}
     </div>
   </div>
 </div>

Class fluid has been used so that our menu items takes the whole width of the screen.

Now we have added all the responsive elements to the page and it’s good to go with all devices.

Additional Resources

Adding a Notification page using Ember.JS to Open Event Front-end

We have added a notification page for users, where they can have a look at their notifications and manage them separately. Here, we have two buttons for either viewing only the unread or all the notifications. The ‘mark all read’ button, as the name suggests will mark all the notifications as  read and is only present in `/notifications/`.

To create the notification page we have three steps

  • Create parent route for notification
  • Create sub routes for notification/all, notification/index
  • Design the notification page

We’ll be generating parents and sub routes by following three commands

ember generate route notifications
ember generate route notifications/index
ember generate route notifications/all
import Ember from ’ember’;

const { Route } = Ember;

export default Route.extend({
titleToken() {
return this.l10n.t(‘Unread’);
},
templateName: ‘notifications/all’,
model() {
return [{
title       : ‘New Session Proposal for event1 by  user1’,
description : ‘Title of proposal’,
createdAt   : new Date(),
isRead      : false
},
{
title       : ‘New Session Proposal for event3 by  user3’,
description : ‘Title of proposal’,
createdAt   : new Date(),
isRead      : false
},
{
title       : ‘New Session Proposal for event5 by  user5’,
description : ‘Title of proposal’,
createdAt   : new Date(),
isRead      : false
}];
}
});

In the js file we have defined a model, and this model is returned whenever the user navigates to this route ie /notifications.

We have used template name attribute to explicitly define template for this route. As /notifications/ and /notifications/all both have almost the same layout with different data, we have used the same template `notifications/all.hbs` for both of them.

{{#each model as |notification|}}

class=”ui segment {{unless notification.isRead ‘blue’}}”>
div class=”ui grid”>
div class=”row”>
div class=”eight wide column”>
h4 class=”ui header”>{{notification.title}}h4>
p>{{notification.description}}p>
div>
div class=”eight wide column right aligned”>
i class=”checkmark box icon”>i>
div>
div>
div class=”row”>
div class=”ten wide column”>
button class=”ui blue button”>{{t ‘View Session’}}button>
div>
div class=”six wide column right aligned”>
p>{{moment-from-now notification.createdAt}}p>
div>
div>
div>

{{/each}}

In the template we have checked if the notification is read or not and accordingly a blue segment is put.

<div class=“ui segment {{unless notification.isRead ‘blue’}}”>

Quick-filter for filtering of events

With a myriad of events present, it might get difficult for users to search for a specific event. So, giving the user a tool to search according to whatever clue they have to search for an event makes it easier for them. Search tool based on location, date (or day) or event’s name or rather a combination of any of these will answer most of the problems to this issue.

To create such a tool we implement a quick-filter component.

An Ember Component is a view that is completely isolated. Properties accessed in its templates got to the view object and actions are targeted at the view object. There is no access to the surrounding context or outer controller, all contextual information must be passed in.

Now, let’s create a component ‘quick-filter’

This command will create 3 files

  1. Quick-filter.js

A JS file is used mainly to run client side JavaScript code on a webpage. The quick-filter component encapsulates certain snippets of Handlebar templates that can be reused in our code. If we need to customize the behaviour of our component we define a subclass of Ember.Component in the app/components

import Ember from ’ember’;
const { Component } = Ember;
export default Component.extend({
tagName   : ‘footer’
classNames: [‘ui’, ‘action’, ‘input’, ‘fluid’]
});

 

  1. Quick-filter-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 ‘All Dates’.

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

moduleForComponent(‘quick-filter’, ‘Integration | Component | quick-filter’);

test(‘it renders’, function(assert) {
this.render(hbs`{{quick-filter}}`);
assert.ok(this.$().html().trim().includes(‘Search’));
});

 

  1. Quick-filter.hbs

Here we design our component. We have used semantic UI elements for designing. Specifically speaking we have used

  • ui-action-input
  • ui-dropdown
  • ui-blue-button-large

Here we have used semantics fluid class to make the component take width of its container.

{{input type=‘text’ placeholder=(t ‘Search for events’)}}
{{#unless device.isMobile }}
 {{input type=‘text’ placeholder=(t ‘Location’)}}
 {{#ui-dropdown class=‘search selection’ selected=filterDate forceSelection=false}}
   {{input type=‘hidden’ id=‘filter_date’ value=filterDate}}
   <i class=“dropdown icon”></i>
   <div class=“default text”>{{t ‘All Dates’}}</div>
   <div class=”menu”>
     <div class=”item” data-value=”all-dates”>{{t ‘All Dates’}}</div>
     <div class=“item” data-value=“today”>{{t ‘Today’}}</div>
     <div class=“item” data-value=“tomorrow”>{{t ‘Tomorrow’}}</div>
     <div class=”item” data-value=”this-week”>{{t ‘This Week’}}</div>
     <div class=“item” data-value=“this-weekend”>{{t ‘This Weekend’}}</div>
     <div class=“item” data-value=“next-week”>{{t ‘Next Week’}}</div>
     <div class=”item” data-value=”this-month”>{{t ‘This Month’}}</div>
   </div>
 {{/ui-dropdown}}
{{/unless}}
<button class=“ui blue button large” type=“button”>{{t ‘Search’}}</button>

Now, our component is ready, and the only part remaining is to place it in our application. We place it in app/templates/index.hbs

<div class=“ui container”>
 {{quick-filter}}
 <h2>{{t ‘Upcoming Events’}}</h2>
 <div class=”ui stackable three column grid”>
   {{#each model as |event|}}
     {{event-card event=event shareEvent=(action ‘shareEvent’)}}
   {{/each}}
 </div>

Now our filter component is up and running.