Knockout seat selector v2

by Dejan Vasic

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
<div class="seat-container">
  <div class="seat-selector"> 
    <div data-bind="foreach: rows">
      <div class="seats-row" data-bind="foreach: seats">
        <div class="seat" data-bind="click: $root.selectSeat, css : { 'not-available' : notAvailable, 'selected' : selected}, style : style">
          <i class="glyphicon glyphicon-check" data-bind="visible: selected()"></i>
          <i class="glyphicon glyphicon-unchecked" data-bind="visible: !selected() && !notAvailable()"></i>
          <i class="fa fa-times" data-bind="visible: notAvailable"></i>
        </div>
      </div>
    </div>
  </div>
</div>

<div class="well" data-bind="visible: readyToProceed">

    <div>
      Selected:
      <div data-bind="foreach: selectedSeats">
        <div>
          seat number <span data-bind="text: id"></span>
        </div>
      </div>
    </div>

    <button class="btn btn-primary">
      Proceed <i class="fa fa-arrow-right"></i>
    </button>
  </div>

CSS

.seat-container{ 
  max-width: 200px;
  overflow-x: auto;
  white-space: nowrap;
}

.seat-selector {
  display: table;
}

.seats-row {
  margin-top: 10px;
  text-align: center;
}

.seat {
  border: 1px solid #eee;
  height: 50px;
  width: 50px;
  border-radius: 10px;
  margin: auto;
  display: inline-block;
  text-align: center;
  padding: 15px 0;
  cursor: pointer;
}

.not-available {
  background-color: #eee;
}

.selected {
  background-color: #a5d8c7;
  border-color: #333;
  border-width: 2px;
}


.ticket-legend {
  width: 20px;
  height: 20px;
  border-radius: 10%;
  background-color: #eee;
  display: inline-block;
  text-align: center;
}

JavaScript

var serverData = {
  name: "The Grand Hotel",
  rows: [{
      name: "A",
      seats: [{
        id: '1',
        notAvailable: true,
        category: 'Vip',
        colourCode: '#E9E581'
      }, {
        id: '2',
        category: 'Vip',
        colourCode: '#E9E581'
      }, {
        id: '3',
        category: 'Vip',
        colourCode: '#E9E581'
      }, {
        id: '4',
        category: 'Vip',
        colourCode: '#E9E581'
      }]
    },

    {
      name: "B",
      seats: [{
        id: '1',
        category: 'General',
        colourCode: '#A3CDDF'
      }, {
        id: '2',
        notAvailable: true,
        category: 'General',
        colourCode: '#A3CDDF'
      }, {
        id: '4',
        category: 'General',
        colourCode: '#A3CDDF'
      }, {
        id: '5',
        category: 'General',
        colourCode: '#A3CDDF'
      }, {
        id: '6',
        category: 'General',
        colourCode: '#A3CDDF'
      }]
    }
  ]
}

function Seat(data, row) {
  var me = this;
  me.id = ko.observable(data.id);
  me.notAvailable = ko.observable(data.notAvailable || false);
  me.selected = ko.observable(data.selected || false);
  me.row = row;
  me.style = ko.observable({
    'background-color': data.notAvailable ? '#eee' : data.colourCode
  });
}

function Row(data) {
  var me = this;
  me.name = ko.observable(data.name);
  me.seats = ko.observableArray();
  for (var i = 0; i < data.seats.length; i++) {
    me.seats.push(new Seat(data.seats[i], me));
  }
}

function SeatSelector(venueDetails, tickets, selections) {
  var me = this;
  me.tickets = tickets;
  me.selectedSeats = ko.observableArray();
  me.venueName = ko.observable(venueDetails.name);
  me.rows = ko.observableArray();
  me.readyToProceed = ko.computed(function() {
    return me.selectedSeats().length > 0;
  });

  for (var i = 0; i < venueDetails.rows.length; i++) {
    me.rows.push(new Row(venueDetails.rows[i]));
  }

  this.selectSeat = function(seat) {
    if...