<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

  <!--Fonts-->
  <link href="https://fonts.googleapis.com/css?family=Roboto|Satisfy" rel="stylesheet">

  <!-- Latest compiled and minified CSS -->
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">

  <!-- Optional theme -->
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
  <link rel="stylesheet" href="./style.css" />

  <!--jquery-->
  <script src="https://code.jquery.com/jquery-3.4.1.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"></script>

  <!--links to Anglular-->
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.10/angular.min.js"></script>

  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.10/angular-messages.min.js"></script>

  <!-- Latest compiled and minified JavaScript -->
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>

  <!-- Angular files -->
  <script src="./script.js"></script>
  <script src="./people.service.js"></script>
  <script src="./app.controller.js"></script>
  <script src="./app.directive.js"></script>
  <script src="./header.directive.js"></script>
  <script src="./sidebar.directive.js"></script>
  <script src="./main-content.directive.js"></script>
</head>

<body>
  <div data-ng-app="contacts">
    <app></app>
  </div>
</body>

</html>
// Code goes here
// angular base module
angular
    .module('contacts', ['ngMessages']);
/* Styles go here */
Your task is to implement the following UI using Angular 1.6.x, JavaScript, CSS and HTML5.

Mockup image: https://www.dropbox.com/s/21r6c10bpr0naud/vlocity-code-challenge-screenshot.PNG?dl=0

Please make a Public Fork of this plunkr and share with us once you're done!

The UI consists of:
- A top bar which consists of a search box to search for a contact, 
  the login user and profile photo.
- The left side bar which consists of a list of full names of contacts.
- The right main panel which consists of the details about the selected contact. 
  The details include the selected contact photo, a button to send a message, 
  rating (number of hearts with a max of 5), a description of the contact. 
  A table of likes and dislikes.

Requirements:
- Each component(sidebar, center panel, header, etc) should be a directive with its own template.
- Fetch the people inside of a Service.
- Make sure the search works, filtering out the people on the list.


Considerations:
- The mockup is just a guideline, you can use any CSS library out there and make it look great (extra points)
- You can use CSS inside the style.scss

Extra points:
- Using SCSS
- Making the UI Reponsive
- Implementing the messaging feature


{
  "People": [
    {
      "name": "Andrew Amernante",
      "rating": 3,
      "img": "http://www.fillmurray.com/200/200",
      "Description": "Gluten free cray cardigan vegan. Lumbersexual pork belly blog, fanny pack put a bird on it selvage",
      "Likes": [
        "Dogs",
        "Long walks on the beach",
        "Chopin",
        "Tacos"
      ],
      "Dislikes": [
        "Birds",
        "Red things",
        "Danish food",
        "Dead Batteries"
      ]
    },
    {
      "name": "Frank Wang",
      "rating": 5,
      "img": "http://www.fillmurray.com/200/201",
      "Description": "Before errors, mails were only pressures. This is not to discredit the idea that a magic is the prose of anelizabeth. This could be, or perhaps some posit the outmost coil to be less than dedal. Some assert that those treatments are nothing more than carp.",
      "Likes": [
        "Frank",
        "Manchester United",
        "Football",
        "Programming"
      ],
      "Dislikes": [
        "Dogs",
        "Long walks on the beach",
        "Chopin",
        "Tacos"
      ]
    },
    {
      "name": "Sissi Chen",
      "rating": 5,
      "img": "http://www.fillmurray.com/200/202",
      "Description": "Aaah! Natural light! Get it off me! Get it off me! Oh, loneliness and cheeseburgers are a dangerous mix. D'oh. Here's to alcohol, the cause of all life's problems.",
      "Likes": [
        "Cats",
        "the beach",
        "Chopin",
        "Blue things"
      ],
      "Dislikes": [
        "Birds"
      ]
    },
    {
      "name": "Diego Garcia",
      "rating": 2,
      "img": "http://www.fillmurray.com/200/204",
      "Description": "Facts are meaningless. You could use facts to prove anything that's even remotely true! I prefer a vehicle that doesn't hurt Mother Earth. It's a go­cart, powered by my ownsense of self­satisfaction. You don't win friends with salad.",
      "Likes": [
        "Talking Spanish",
        "Spanish food",
        "Spanish things",
        "Football"
      ],
      "Dislikes": [
        "Not talking spanish",
        "Chopin"
      ]
    },
    {
      "name": "Fuad Rashid",
      "rating": 4,
      "img": "http://www.fillmurray.com/200/206",
      "Description": "Gluten free cray cardigan vegan. Lumbersexual pork belly blog, fanny pack put a bird on it selvage",
      "Likes": [
        "Dogs",
        "Long walks on the beach",
        "Chopin",
        "Tacos"
      ],
      "Dislikes": [
        "Birds",
        "Red things",
        "Danish food",
        "Dead Batteries"
      ]
    }
  ]
}
<div class="content-wrapper">
    <header></header>
    <div class="col-xs-12 col-md-12 col-lg-12">
        <sidebar></sidebar>
        <main-content></main-content>
    </div>
</div>
<nav class="navbar navbar-default" role="navigation">
    <div class="navbar-header col-xs-12">
        <div class="search-container col-xs-12 col-sm-3">
            <div class="input-group">
                <span class="input-group-addon" id="search"><i class="glyphicon glyphicon-search"></i></span>
                <input type="text" class="search form-control" placeholder="Search" aria-describedby="search" />

            </div>
        </div>
        <div class="user-container col-xs-12 col-sm-9">
            <span>{{ vm.activeContact.name }}</span>
            <span class="glyphicon glyphicon-user"></span>
            <span class="glyphicon glyphicon-chevron-down"></span>
        </div>
    </div>
</nav>
<aside class="col-xs-12 col-sm-3">
    <ul class="contact-list list-group">
        <li ng-repeat="person in vm.people track by $index" ng-click="$parent.vm.selectPerson($index)" class="list-group-item"
            ng-class="{'active': person.active}">
            <span>{{ person.name }}</span>
            <span class="glyphicon glyphicon-chevron-right pull-right"></span>
        </li>
    </ul>
</aside>
(function () {
    'use strict';

    angular
        .module('contacts')
        .controller('AppController', ['$rootScope', '$scope', 'PeopleService', function ($rootScope, $scope, PeopleService) {

            var vm = this;
            vm.totalRatings = new Array(5); // set up a blank array with 5 elements, 5 hearts
            vm.people = [];

            vm.activeContact = {};

            // default message object for message form
            vm.message = {
                email: '',
                subject: '',
                message: ''
            };

            // get the data on directive intitialization     
            PeopleService.getData().then(function (response) {
                vm.people = response;
                vm.activeContact = vm.people[0]; // set default active contact to FIRST person in the object.                
            })
                .catch(function (error) {
                    console.log(error);
                });

            function resetActiveFlags() {
                angular.forEach(vm.people, function (person, index) {
                    if (person.active) {
                        person.active = !person.active;
                    }
                });
            }

            vm.selectPerson = function (id) {
                console.log('key: ' + id);
                resetActiveFlags();
                angular.forEach(vm.people, function (person, index) {
                    if (index === id) {
                        person.active = true;
                        vm.activeContact = person;

                    }
                });
            };

            vm.updateRating = function (number) {
                vm.activeContact.rating = number;
            };

            vm.submitForm = function (isValid) {
                if (isValid) {

                }
            };

            $rootScope.$on('searchStringUpdated', function (event, message) {
                if (message !== '') {  // if search string is not empty, filter the list
                    var filtered = vm.people.filter(function (person) {
                        return person.name.toLowerCase().includes(message);
                    });
                    vm.people = filtered;
                    $scope.$apply();
                } else { // else get the whole list again
                    PeopleService.getData().then(function (response) {
                        vm.people = response;
                    })
                        .catch(function (error) {
                            console.log(error);
                        });
                }
                vm.activeContact = vm.people[0];
            });
        }])
})();
(function () {
    'use strict';

    angular
        .module('contacts')
        .directive('app', function () {
            return {
                restrict: 'E',
                controller: 'AppController',
                controllerAs: 'vm',
                templateUrl: 'app.template.html'
            };
        });


})();
(function () {
    'use strict';

    angular
        .module('contacts')
        .directive('header', ['$rootScope', function ($rootScope) {
            return {
                restrict: 'E',
                templateUrl: 'header.template.html',
                link: function (scope, element, attrs, controller) {
                    element.find('input').on('keyup', function (event) {
                        $rootScope.$broadcast('searchStringUpdated', this.value);
                    });
                }
            };
        }]);


})(); 
<div class="content-body col-sx-12 col-sm-9">
    <div class="col-xs-12">
        <div class="col-xs-6 col-sm-4">
            <img src="{{ vm.activeContact.img }}" alt="{{ vm.activeContact.name }}" class="thumbnail" />
        </div>
        <div class="col-xs-6 col-sm-4">
            <div>
                <button class="btn btn-primary" data-toggle="modal" data-target="#message-modal">SEND MESSAGE!</button>
            </div>
            <div>
                <ul class="ratings list-horizontal">
                    <li ng-repeat="r in vm.totalRatings track by $index" class="glyphicon" ng-class="{'glyphicon-heart': $index  <= vm.activeContact.rating, 'glyphicon-heart-empty': $index > vm.activeContact.rating}"
                        ng-click="vm.updateRating( $index )" title="{{ $index + 1 }} heart rating">
                    </li>
                </ul>
            </div>
        </div>

        <div class="col-xs-12">
            <p class="description">{{ vm.activeContact.Description }}</p>
        </div>
        <div class="table-container col-xs-12">
            <table class="table table-striped table-bordered">
                <thead>
                    <tr>
                        <th>Likes</th>
                        <th>Dislikes</th>
                    </tr>
                </thead>
                <tbody>
                    <tr ng-repeat="row in vm.activeContact.Likes track by $index">
                        <td>{{vm.activeContact.Likes[ $index ] }}</td>
                        <td>{{vm.activeContact.Dislikes[ $index ] }}</td>
                    </tr>
                </tbody>
            </table>

        </div>

        <div id="message-modal" class="modal fade" role="dialog">
            <div class="modal-dialog" role="document">
                <div class="modal-content">
                    <div class="modal-header">
                        <h4 class="modal-title pull-left">Send {{ vm.activeContact.name }} a message!</h4>
                        <button type="button" class="close" data-dismiss="modal"><span class="glyphicon glyphicon-remove"></span></button>
                    </div>
                    <div class="modal-body">
                        <form name="messageform" novalidate>
                            <div class="form-group" ng-class="{ 'has-error': messageform.email.$touched && messageform.email.$invalid }">
                                <label for="email">Your email address</label>
                                <input type="email" name="email" id="email" ng.model="vm.message.email" class="form-control"
                                    required="required" />
                                <div ng-messages="messageform.email.$error">
                                    <p ng-message="required">Your email address is required.</p>
                                </div>

                            </div>
                            <div class="form-group" ng-class="{ 'has-error': messageform.subject.$touched && messageform.subject.$invalid }">
                                <label for="subject">Subject</label>
                                <input type="text" name="subject" id="subject" class="form-control" ng.model="vm.message.subject"
                                    required="required" />
                                <div ng-messages="messageform.subject.$error">
                                    <p ng-message="required">A subject line is required.</p>
                                </div>
                            </div>
                            <div class="form-group" ng-class="{ 'has-error': messageform.message.$touched && messageform.message.$invalid }">
                                <label for="message">Message</label>
                                <textarea name="message" id="message" class="form-control" ng.model="vm.message.message"
                                    required="required"></textarea>
                                <div ng-messages="messageform.message.$error">
                                    <p ng-message="required">Please enter a message to send.</p>
                                </div>
                            </div>
                            <div class="form-group">
                                <button id="cancel" class="btn btn-default" data-dismiss="modal"><i class="glyphicon glyphicon-remove"></i>cancel</button>
                                <button type="submit" id="submit" class="btn btn-primary" ng-disabled="messageform.$invalid"><i
                                        class="glyphicon glyphicon-envelope"></i>Send Message!</button>
                            </div>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
(function () {
    'use strict';

    angular
        .module('contacts')
        .directive('mainContent', function () {
            return {
                restrict: 'E',
                templateUrl: 'main-content.template.html'
            };
        });
})();

( function () {
    'use strict';

    angular
        .module('contacts')      
        .service( 'PeopleService', PeopleService );


   /** @ngInject */
    function PeopleService( $http, $q )  {
        var service = {}; // set up a service object to hold private methods and properties
        
        service.people = undefined; // establish private property on service object to rcache our ajax response

        // cachePeople receives ajax response, expands data and copies People object into private property, then returns the data.
        service.cachePeople = function ( response ) {
            service.people = [];
            
            angular.forEach( response.data.People, function ( person, index ) {
                person.active = false; // add an active property to existing object
                service.people.push( person );
            }); 
            service.people[ 0 ].active = true; // set first contact as active by default
            return service.people; 
        };

        return {
            getData: function () {       
                if( service.people ) {   // if the people array already exists on the service object, return the data                 
                    return $q.when( service.people ); 
                } else { // else run the ajax request and cache the data.
                    return $http
                        .get( './people.json')
                        .then( function( response ) {
                            return service.cachePeople( response );
                        })
                        .catch( function( error ) {
                            console.log( error );
                        });
                }          
            },

        };
    }
})();
(function () {
    'use strict';

    angular
        .module('contacts')
        .directive('sidebar', function () {
            return {
                restrict: 'E',
                templateUrl: 'sidebar.template.html'
            };
        });
})();

body {
  font-family: 'Roboto', sans-serif;
  font-size: 12px;
  color: black;
  background-image: url("../IMG/team2.jpg");
  background-repeat: no-repeat;
  background-position: center center;
  background-attachment: fixed;
  -webkit-background-size: cover;
  -moz-background-size: cover;
  -o-background-size: cover;
  background-size: cover;
}

.thumbnail {
  border-radius: 10px;
  width: 100px;
}

header nav {
  opacity: 0.8;
}

.navbar-header {
  padding: 20px 0;
  background: #3a4257;
  color: white;
}

.search-container .input-group-addon {
  background: #fff;
  border-right: none;
  border-radius: 20px 0 0 20px;
}

.search-container input:focus,
.search-container input:active {
  border: 1px solid #ccc;
  border-left: none;
  box-shadow: none;
  outline: none;
}

.search-container .search {
  border-left: none;
  border-radius: 0 20px 20px 0;
}

.navbar-header .user-container {
  text-align: right;
}

.contact-list li {
  padding: 25px;
  color: white;
  opacity: 0.7;
  background: #3A4257;
}

.contact-list li:hover,
.contact-list li:active,
.contact-list li.active,
.contact-list li.active:hover,
.contact-list li.active:active {
  background: white;
  border: #ddd;
  color: #3A4257;
  opacity: 0.7;
}

.contact-list li:hover {
  cursor: pointer;
}

.ratings {
  margin: 5px 5px;
  padding: 0;
  text-align: left;
}

.ratings li {
  font-size: 15px;
  padding-right: 5px;
  color: white;
}

.ratings li:hover {
  cursor: pointer;
}

.ratings li:last-child {
  padding-right: 0;
}

.description {
  padding: 10px;
  background-color: white;
  color: #3A4257;
  opacity: 0.7;
}

.table-container {
  margin-top: 100px;
}

.table-striped > tbody > tr:nth-of-type(odd) {
  background-color: #F8F5F0;
  opacity: 0.7;
  color: #3A4257;
}

.table-striped > tbody > tr:nth-of-type(even) {
  background-color: #3A4257;
  opacity: 0.7;
  color: white;
}

.table-striped > thead {
  background-color: #3A4257;
  color: white;
}

.table-striped > thead th {
  width: 50%;
}

#cancel i:before,
#submit i:before {
  margin-right: 10px;
}