JSFiddle - React, Tailwind, and code Playground

by kyllle

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0-alpha1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.2.2/backbone-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.marionette/2.4.3/backbone.marionette.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/3.0.3/normalize.css">
<div class="js-ctn"></div>

<!-- CompositeView Template -->
<script type="text/html" class="js-persons-tmpl">
    <table> 
        <tbody class="js-tbody">
        </tbody>
    </table>
</script>


<!-- ItemView Template -->
<script type="text/html" class="js-person-tmpl">
    <% _.each(bookedClasses, function(booked, i) { %>
    <tr> 
        <% if(i == 0) { %>
            <td rowspan="4"><%= name %></td>
        <% } %>
        <td>
            <%= booked %>
            <button class="action action--remove js-action--remove"><svg viewBox="0 0 24 24"><g><path d="M12,2C6.5,2,2,6.5,2,12c0,5.5,4.5,10,10,10s10-4.5,10-10C22,6.5,17.5,2,12,2z M16.9,15.5l-1.4,1.4L12,13.4l-3.5,3.5   l-1.4-1.4l3.5-3.5L7.1,8.5l1.4-1.4l3.5,3.5l3.5-3.5l1.4,1.4L13.4,12L16.9,15.5z"/></g></svg></button>
        </td>
        <td>
            <select class="js-sessions-select" data-classid="<%= booked %>">
                <option>Please select...</option>
                <% _.each(availableClasses, function(available) { %>
                    <option value="<%= available.id %>"><%= available.name %> --- <%= availableSessions %></option>
                <% }) %>
            </select>
        </td>
    </tr>
    <% }) %> 
</script>

CSS

@import url(http://fonts.googleapis.com/css?family=Roboto:300, 400, 500);
 body {
    font-family: Roboto;
    color: #191919;
}
a {
    color: #191919;
}

svg {
    overflow: hidden;
    vertical-align: middle;
    position: relative;
    top: -1px;
    box-sizing: content-box;
    pointer-events: none;
}

/* Actions */
.action {
    border: none;
    outline: none;
    background: none;
}

.action--remove {
    padding: 0;
    position: absolute;
    top: 50%;
    right: 0;
    transform: translateY(-50%);
}

.action--remove svg {
   width: 15px;
   fill: black;
}
/* General Table Styles */
 table {
    margin: 0;
    padding: 0;
    width: 100%;
}
table th, table td {
    padding: 10px 20px;
    text-align: left;
    border-bottom: 1px solid #ccc;
}
table th {
    border-width: 2px;
}
table td {
    color: #666;
    position: relative;
    vertical-align: middle;
}
table tr:last-child th, table tr:last-child td {
    border-bottom: none;
}
table tr:nth-child(even) {
    background: #eee;
}


select.error {
    border: 1px solid red;
}

JavaScript

console.clear();

'use strict';

// Dummy data
var availableClassesData = [{
    "id": 34,
    "name": "Core Evo"
}, {
    "id": 686,
    "name": "Core Body Workout"
}, {
    "id": 870,
    "name": "Boxercise"
}, {
    "id": 1345,
    "name": "Rapid Circuits"
}];

var personsData = [{
    "id": 1,
    "name": "Kenneth Wise",
    "bookedClasses": [
        "375",
        "980",
    	"1072",
    	"1320"],
    "cancelledClasses": [],
    "swappedClasses": {},
    "availableSessions": 1
}];

// Model Class
var Person = Backbone.Model.extend({});
var FitnessClass = Backbone.Model.extend({});

// Collection Class
var Persons = Backbone.Collection.extend({
    model: Person
});

var FitnessClasses = Backbone.Collection.extend({
    model: FitnessClass
});

var PersonView = Marionette.ItemView.extend({
    
    events: {
        'change .js-sessions-select': 'onSessionSelect'
	},

    template: '.js-person-tmpl',

    templateHelpers: function () {
        return {
            availableClasses: this.options.availableClasses.toJSON()
        }
    },

    initialize: function () {
        //console.log('PersonView::initialize', this.model.toJSON());
    },
    
    onSessionSelect: function(event) {
		
        var $currentTarget	= $(event.currentTarget),
            classId = $currentTarget.val(),
            availableSessions = this.model.get('availableSessions');
        
        
        var selectedOption = this.$el.find('.js-sessions-select option[value="'+classId+'"]:selected'),
        	len = selectedOption.length;       
        
        if(len > availableSessions) {
            selectedOption.parent().addClass('error');
        } else {
            selectedOption.parent().removeClass('error');
        }
    }
});

// View Class
var PersonsView = Marionette.CompositeView.extend({

    template: '.js-persons-tmpl',
    childView: PersonView,
    childViewContainer: '.js-tbody',

    childViewOptions: function () {
        return {
            availableClasses:...