diff --git a/index.html b/index.html
index 80e9751..ec23d27 100644
--- a/index.html
+++ b/index.html
@@ -8,16 +8,16 @@
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
crossorigin="anonymous">
-
+
-
-
Content will be here
+
+
diff --git a/js/app.js b/js/app.js
deleted file mode 100644
index eb109ab..0000000
--- a/js/app.js
+++ /dev/null
@@ -1,2 +0,0 @@
-'use strict';
-
diff --git a/js/main.js b/js/main.js
new file mode 100644
index 0000000..2d9f8a9
--- /dev/null
+++ b/js/main.js
@@ -0,0 +1,5 @@
+import PhonesPage from './phones/phones-page.js'
+
+const phonesPage = new PhonesPage ({
+ element: document.querySelector('[data-page-container')
+})
diff --git a/js/phones/components/component.js b/js/phones/components/component.js
new file mode 100644
index 0000000..35b0231
--- /dev/null
+++ b/js/phones/components/component.js
@@ -0,0 +1,54 @@
+export default class Component {
+ constructor({ element }) {
+ this._callbackMap = {};
+ this._element = element;
+ }
+
+ on(eventName, selector, callback) {
+ this._element.addEventListener(eventName, (event) => {
+ const deligatedTarget = event.target.closest(selector);
+ if (!deligatedTarget) {
+ return;
+ }
+ callback(event);
+
+ })
+ }
+
+ emit(eventName, data) {
+ const callback = this._callbackMap[eventName];
+ if(!callback) {
+ return;
+ }
+ this._callbackMap[eventName].forEach(callback => {
+ callback(data)
+ });
+ }
+
+ subscribe(eventName, callback) {
+ if(!this._callbackMap[eventName]) {
+ this._callbackMap[eventName] = [];
+ }
+
+ this._callbackMap[eventName].push(callback);
+
+ }
+
+ unsubscribe(eventName, callbackToRemove) {
+ const callback = this._callbackMap[eventName];
+ if(callback) {
+ this._callbackMap[eventName] = callback
+ .filter( cb => cb !== callbackToRemove
+ )
+ }
+ }
+
+ hide() {
+ this._element.hidden = true;
+ }
+
+ show() {
+ this._element.hidden = false;
+ }
+
+}
\ No newline at end of file
diff --git a/js/phones/components/filter.js b/js/phones/components/filter.js
new file mode 100644
index 0000000..5c1b714
--- /dev/null
+++ b/js/phones/components/filter.js
@@ -0,0 +1,48 @@
+import Component from "./component.js";
+import utils from "../../utils.js";
+
+export default class Filter extends Component {
+ constructor({ element }) {
+ super({ element });
+
+ this._render();
+
+ this._queryField = this._element.querySelector('[data-element="query-fild"]');
+ this._orderField = this._element.querySelector('[data-element="order-fild"]');
+
+ const debounceOnInput = utils.debounce(() => {
+ this.emit('query-change')
+ } ,500)
+
+ this.on('input', '[data-element="query-fild"]', debounceOnInput);
+
+ this.on('change', '[data-element="order-fild"]',() => {
+ this.emit('order-change')
+ });
+ }
+
+ getCurrent() {
+ return {
+ query: this._queryField.value,
+ order: this._orderField.value
+ }
+ }
+
+ _render() {
+ this._element.innerHTML = `
+
+ Search:
+
+
+
+
+ Sort by:
+
+ Alphabetical
+ Newest
+
+
+ `
+ }
+}
+
diff --git a/js/phones/components/phone-viewer.js b/js/phones/components/phone-viewer.js
new file mode 100644
index 0000000..233bd3c
--- /dev/null
+++ b/js/phones/components/phone-viewer.js
@@ -0,0 +1,58 @@
+import Component from './component.js'
+
+export default class PhoneViewer extends Component{
+ constructor({
+ element
+ }) {
+ super({element}),
+
+ this.on('click', '[data-batton="back-button"]', () => {
+ this.emit('back');
+ })
+
+ this.on('click', '[data-element="small-preview"]', (event) =>{
+ this.bigImg = this._element.querySelector('[data-element="big-preview"]');
+ this.bigImg.src = event.target.src;
+ })
+
+ this.on('click', '[data-batton="add-to-basket"]', () => {
+ this.emit('add-to-basket',this._phoneDetaild.id);
+ })
+
+ }
+
+ show(phoneDetails) {
+ super.show();
+ this._phoneDetaild = phoneDetails;
+ this._render();
+ }
+
+ _render() {
+ this._element.innerHTML = `
+
+
+
Back
+
Add to basket
+
+
+
${this._phoneDetaild.name}
+
+
Motorola XOOM with Wi-Fi has a super-powerful dual-core processor and Android™ 3.0 (Honeycomb) — the Android platform designed specifically for tablets. With its 10.1-inch HD widescreen display, you’ll enjoy HD video in a thin, light, powerful and upgradeable tablet.
+
+
+ ${
+ this._phoneDetaild.images.map( src => `
+
+
+
+ `
+ ).join('')
+ }
+
+ `
+ }
+}
\ No newline at end of file
diff --git a/js/phones/components/phones-catalog.js b/js/phones/components/phones-catalog.js
new file mode 100644
index 0000000..741016f
--- /dev/null
+++ b/js/phones/components/phones-catalog.js
@@ -0,0 +1,75 @@
+import Component from './component.js'
+
+export default class PhonesCatalog extends Component{
+ constructor({element}) {
+
+ super({ element });
+
+ this._phones = [];
+ this._render();
+
+ this.on('click', '[data-element="details-link"]', () => {
+
+ const phoneEl = event.target.closest('[data-element="phone-elnment"]');
+ const phoneId = phoneEl.dataset.phoneId;
+ this.emit('phone-selected', phoneId);
+ })
+
+ this.on('click', '[data-element="add-phone-to-basket"]', () => {
+ const phoneEl = event.target.closest('[data-element="phone-elnment"]');
+ const phoneId = phoneEl.dataset.phoneId;
+ this.emit('add-to-basket', phoneId);
+ })
+
+ }
+
+ show(phones) {
+ this._phones = phones;
+ super.show();
+ this._render();
+ }
+
+
+ _render() {
+ this._element.innerHTML = `
+
+ ${
+
+ this._phones.map(phone => `
+
+
+
+
+
+
+
+ ${phone.name}
+ ${phone.snippet}
+
+ `
+ ).join('')
+ }
+
+
+
+ `
+ }
+}
\ No newline at end of file
diff --git a/js/phones/components/shoping-cart.js b/js/phones/components/shoping-cart.js
new file mode 100644
index 0000000..df72dbc
--- /dev/null
+++ b/js/phones/components/shoping-cart.js
@@ -0,0 +1,54 @@
+import Component from "./component.js"
+
+export default class ShoppingCart extends Component {
+ constructor( {element} ) {
+ super( {element} )
+ this.allPhoneInBasket = {};
+ this._render();
+ this.on('click', '[data-element ="remove-button"]', (event) => {
+ const phone = event.target.closest('li');
+ this.remove(phone.dataset.elementId);
+ })
+ }
+
+
+ addToBasket(selectedPhone) {
+ if(!this.allPhoneInBasket.hasOwnProperty(selectedPhone)) {
+ this.allPhoneInBasket[selectedPhone] = 0;
+ }
+ this.allPhoneInBasket[selectedPhone] += 1;
+ this._render();
+
+ }
+
+ remove(phone) {
+ if(this.allPhoneInBasket.hasOwnProperty(phone)) {
+ this.allPhoneInBasket[phone] -= 1;
+ }
+
+ if(this.allPhoneInBasket[phone] === 0) {
+ delete this.allPhoneInBasket[phone]
+ }
+ this._render()
+
+ }
+
+ _render() {
+ this._element.innerHTML = `
+
Shopping Cart
+
+ ${
+ Object.entries(this.allPhoneInBasket)
+ .map(([name, quantity]) => `
+
+ ${name} - ${quantity}
+ X
+ `)
+ .join('')
+ }
+
+ `
+ }
+}
\ No newline at end of file
diff --git a/js/phones/phones-page.js b/js/phones/phones-page.js
new file mode 100644
index 0000000..f650164
--- /dev/null
+++ b/js/phones/phones-page.js
@@ -0,0 +1,108 @@
+'use strict'
+import PhonesCatalog from './components/phones-catalog.js';
+import PhoneService from './service/phones-service.js';
+import PhoneVieWer from './components/phone-viewer.js'
+import ShoppingCart from './components/shoping-cart.js';
+import Filter from './components/filter.js';
+
+export default class PhonesPage{
+ constructor({ element }) {
+ this._element = element;
+ this._render();
+
+ this._initFilter();
+ this._initCatalog();
+ this._initVeiwer();
+ this._initCart();
+
+ }
+
+ _initCatalog() {
+ this._catalog = new PhonesCatalog({
+ element: this._element.querySelector('[data-component = "phone-catalog"]')
+ })
+
+ this._showPhones();
+ this._catalog.subscribe('phone-selected', (id) =>{
+ console.log('selected ' + id)
+ const phoneDetails = PhoneService.getById(id);
+ this._catalog.hide();
+ this._viewer.show(phoneDetails)
+ });
+
+ this._catalog.subscribe('add-to-basket',(selectedPhone) => {
+ this._cart.addToBasket(selectedPhone);
+ })
+ };
+
+ _initVeiwer() {
+ this._viewer = new PhoneVieWer({
+ element: this._element.querySelector('[data-component = "phone-viewer"]'),
+ });
+
+ this._viewer.subscribe('back', () => {
+ this._showPhones();
+ this._viewer.hide();
+ })
+
+ this._viewer.subscribe('add-to-basket',(selectedPhone) => {
+ this._cart.addToBasket(selectedPhone);
+ })
+
+ }
+
+ _initCart() {
+ this._cart = new ShoppingCart({
+ element: this._element.querySelector('[data-component = "shoping-cart"]')
+ });
+
+ }
+
+ _initFilter() {
+ this._filter = new Filter({
+ element: this._element.querySelector('[data-component="filter"]')
+ })
+
+ this._filter.subscribe('query-change', (eventData) => {
+ this._showPhones();
+ })
+
+ this._filter.subscribe('order-change', (eventData) => {
+ this._showPhones();
+ })
+ }
+
+ _showPhones() {
+ this._currentFiltering = this._filter.getCurrent();
+ PhoneService.getAll(this._currentFiltering).then((phones) => {
+ console.log(this._currentFiltering)
+ this._catalog.show(phones);
+ });
+
+ }
+
+
+ _render() {
+ this._element.innerHTML = `
+
+ `
+ }
+}
\ No newline at end of file
diff --git a/js/phones/service/phones-service.js b/js/phones/service/phones-service.js
new file mode 100644
index 0000000..598ff6e
--- /dev/null
+++ b/js/phones/service/phones-service.js
@@ -0,0 +1,107 @@
+import utils from "../../utils.js";
+
+let phonesFromServer;
+const phoneByIdDetails = {
+ "additionalFeatures": "Adobe\u00ae Flash\u00ae Player compatible; 1.3MP front-facing camera for video chat; eReader pre-loaded; Swype text input technology\r\n",
+ "android": {
+ "os": "Android 2.2",
+ "ui": "TouchWiz"
+ },
+ "availability": [
+ "AT&T,",
+ "Sprint,",
+ "T-Mobile,",
+ "Verizon"
+ ],
+ "battery": {
+ "standbyTime": "780 hours",
+ "talkTime": "",
+ "type": "Lithium Ion (Li-Ion) (4000 mAH)"
+ },
+ "camera": {
+ "features": [
+ "Flash",
+ "Video"
+ ],
+ "primary": "3.0 megapixels"
+ },
+ "connectivity": {
+ "bluetooth": "Bluetooth 3.0",
+ "cell": "AT&T: GSM/EDGE : 850/900/1800/1900; 3G : 850/1900/2100\r\n\r\nSprint: CDMA : 850/1900MHz\r\n\r\nT-Mobile: GSM/EDGE : 850/900/1800/1900; 3G : 1700/1900\r\n\r\nVerizon: CDMA : 800MHz/1900MHz",
+ "gps": true,
+ "infrared": false,
+ "wifi": "802.11 b/g/n"
+ },
+ "description": "Feel Free to Tab\u2122. The Samsung Galaxy Tab\u2122, the tablet device that delivers enhanced capabilities with advanced mobility, has a large, perfectly sized, 7.0\" screen that offers plenty of room for the thousands of interactive games and apps available for the Android\u2122 platform, and its slim design makes it perfect for travel and one-handed grip. Use the Galaxy Tab to relax and enjoy an e-book, watch rich video or full web content with its Adobe\u00ae Flash\u00ae Player compatibility, video chat using the front-facing camera, or send user-generated content wirelessly to other devices like your TV via AllShare\u2122. With so many options for customization and interactivity, the Galaxy Tab gives you everything you want, anywhere you go\u2026Feel Free to Tab\u2122.",
+ "display": {
+ "screenResolution": "WSVGA (1024 x 600)",
+ "screenSize": "7.0 inches",
+ "touchScreen": true
+ },
+ "hardware": {
+ "accelerometer": true,
+ "audioJack": "3.5mm",
+ "cpu": "1GHz",
+ "fmRadio": false,
+ "physicalKeyboard": false,
+ "usb": "USB 2.0"
+ },
+ "id": "samsung-galaxy-tab",
+ "images": [
+ "img/phones/samsung-galaxy-tab.0.jpg",
+ "img/phones/samsung-galaxy-tab.1.jpg",
+ "img/phones/samsung-galaxy-tab.2.jpg",
+ "img/phones/samsung-galaxy-tab.3.jpg",
+ "img/phones/samsung-galaxy-tab.4.jpg",
+ "img/phones/samsung-galaxy-tab.5.jpg",
+ "img/phones/samsung-galaxy-tab.6.jpg"
+ ],
+ "name": "Samsung Galaxy Tab\u2122",
+ "sizeAndWeight": {
+ "dimensions": [
+ "120.39 mm (w)",
+ "189.99 mm (h)",
+ "11.9 mm (d)"
+ ],
+ "weight": "379.88 grams"
+ },
+ "storage": {
+ "flash": "16384MB",
+ "ram": "640MB"
+ }
+ };
+
+const API_URL ='https://mate-academy.github.io/phone-catalogue-static/api';
+
+const PhoneService = {
+ getAll({query = '', order = ''} = {}) {
+
+ return fetch(API_URL + '/phones.json')
+ .then((response) => response.json())
+ .then((phones) => {
+ phonesFromServer = phones;
+ })
+ .then(() => {
+ const firltredPhones = phonesFromServer.filter((phone) => {
+ return phone.name.toLowerCase().includes(query.toLowerCase());
+ })
+ const sortedPhones = firltredPhones;
+
+ if(order === 'age') {
+ utils.sortFromAge(sortedPhones);
+ } else {
+ utils.sortFromName(sortedPhones);
+ }
+
+ return sortedPhones;
+ })
+
+ },
+
+ getById(id) {
+ return phoneByIdDetails;
+ }
+
+}
+
+export default PhoneService;
\ No newline at end of file
diff --git a/js/utils.js b/js/utils.js
new file mode 100644
index 0000000..a151a24
--- /dev/null
+++ b/js/utils.js
@@ -0,0 +1,35 @@
+const utils = {
+ debounce,
+ sortFromAge,
+ sortFromName
+}
+
+export default utils;
+
+function debounce(f, delay) {
+ let timer = null;
+ return function(...args){
+ clearTimeout(timer);
+ timer = setTimeout(() => {
+ f.call(this, ...args)
+ },delay)
+ }
+}
+
+function sortFromAge(arr) {
+ arr.sort((a,b) => {
+ if(a.age > b.age) return 1;
+ if(a.age < b.age) return -1;
+ return 0
+ })
+}
+
+function sortFromName(arr) {
+ arr.sort((a,b) => {
+ if(a.name > b.name) return 1;
+ if(a.name < b.name) return -1;
+ return 0
+ })
+}
+
+