Explorar o código

Turn the whole thing into an e-commerce app

Benoît Hubert %!s(int64=8) %!d(string=hai) anos
pai
achega
a237863b0e

+ 2 - 1
.gitignore

@@ -1,4 +1,5 @@
 # Include your project-specific ignores in this file
 # Read about how to use .gitignore: https://help.github.com/articles/ignoring-files
 # Useful .gitignore templates: https://github.com/github/gitignore
-doc/
+doc/
+node_modules/

+ 0 - 28
css/styles.css

@@ -1,28 +0,0 @@
-body {
-	background: #f4f4f4;
-	font-family: Arial, Helvetica;
-}
-.container {
-  margin: 150px auto;
-	background: #fff;
-	border: 1px solid #eee;
-	padding: 30px;
-	width: 600px;
-}
-button {
-  padding: 10px;
-  color: white;
-  border: none;
-  border-radius: 3px;
-}
-.blue {
-  background: #24d;
-}
-.red {
-  background: #d42;
-}
-input {
-  border: 1px solid #ddd;  
-  padding: 8px;
-  border-radius: 3px;
-}

+ 0 - 88
index.html

@@ -1,88 +0,0 @@
-<!doctype html>
-<html class="no-js" lang="">
-    <head>
-        <meta charset="utf-8">
-        <meta http-equiv="x-ua-compatible" content="ie=edge">
-        <title>Formation jQuery</title>
-        <meta name="description" content="">
-        <meta name="viewport" content="width=device-width, initial-scale=1">
-
-        <link rel="manifest" href="site.webmanifest">
-        <link rel="apple-touch-icon" href="icon.png">
-        <!-- Place favicon.ico in the root directory -->
-
-        <link rel="stylesheet" href="css/normalize.css">
-        <link rel="stylesheet" href="css/main.css">
-        <link rel="stylesheet" href="css/styles.css">
-    </head>
-    <body>
-        <!--[if lte IE 9]>
-            <p class="browserupgrade">You are using an <strong>outdated</strong> browser. Please <a href="https://browsehappy.com/">upgrade your browser</a> to improve your experience and security.</p>
-        <![endif]-->
-
-        <!-- Add your site or application content here -->
-        <div class="container">
-            <p>Hello world! This is HTML5 Boilerplate.</p>
-            <p>Today's date: <span id="todaysDate"></span>.</p>
-            <p>Enter your name please:</p>
-            <form id="helloForm">
-                <input id="inputName" value="" />
-                <button class="blue" type="submit">Say hello again</button>
-            </form>
-            <button class="red" id="clearForm" type="button">Clear form</button>
-
-            <div id="ajax"></div>
-            <button class="blue" id="getAjax" type="button">Get HTML with Ajax</button>
-        </div>
-        <script src="js/vendor/modernizr-3.5.0.min.js"></script>
-        <script src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
-        <script>window.jQuery || document.write('<script src="js/vendor/jquery-3.2.1.min.js"><\/script>')</script>
-        <script src="js/plugins.js"></script>
-        <script src="js/main.js"></script>
-
-        <!-- Google Analytics: change UA-XXXXX-Y to be your site's ID. -->
-        <script>
-            window.ga=function(){ga.q.push(arguments)};ga.q=[];ga.l=+new Date;
-            ga('create','UA-XXXXX-Y','auto');ga('send','pageview')
-        </script>
-        <script src="https://www.google-analytics.com/analytics.js" async defer></script>
-        <script>
-          $(document).ready(function() {
-
-            var form = $('#helloForm');
-            var input = $('#inputName');
-            var clearButton = $('#clearForm');
-            var dateSpan = $('#todaysDate');
-            var ajaxButton = $('#getAjax');
-
-            function sayHelloAgain(e) {
-                e.preventDefault();
-                alert('Hello ' + input.val());
-            }
-
-            function clearForm() {
-                input.val('');
-            }
-
-            function showDate() {
-              var date = new Date().toString();
-              dateSpan.html(date);
-            }
-
-            showDate();
-            setInterval(showDate, 1000);
-
-            function loadDoc() {
-              $.get('some-text.html', function(html) {
-                $('#ajax').html(html);
-              });
-            }
-
-            form.submit(sayHelloAgain);
-            clearButton.click(clearForm);
-            ajaxButton.click(loadDoc);
-
-          });
-        </script>
-    </body>
-</html>

+ 355 - 0
package-lock.json

@@ -0,0 +1,355 @@
+{
+  "requires": true,
+  "lockfileVersion": 1,
+  "dependencies": {
+    "accepts": {
+      "version": "1.3.4",
+      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz",
+      "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=",
+      "requires": {
+        "mime-types": "2.1.17",
+        "negotiator": "0.6.1"
+      }
+    },
+    "array-flatten": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+      "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
+    },
+    "body-parser": {
+      "version": "1.18.2",
+      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz",
+      "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=",
+      "requires": {
+        "bytes": "3.0.0",
+        "content-type": "1.0.4",
+        "debug": "2.6.9",
+        "depd": "1.1.1",
+        "http-errors": "1.6.2",
+        "iconv-lite": "0.4.19",
+        "on-finished": "2.3.0",
+        "qs": "6.5.1",
+        "raw-body": "2.3.2",
+        "type-is": "1.6.15"
+      }
+    },
+    "bytes": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
+      "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
+    },
+    "content-disposition": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
+      "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ="
+    },
+    "content-type": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+      "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
+    },
+    "cookie": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
+      "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
+    },
+    "cookie-signature": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+      "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
+    },
+    "debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "requires": {
+        "ms": "2.0.0"
+      }
+    },
+    "depd": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz",
+      "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k="
+    },
+    "destroy": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
+      "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
+    },
+    "ee-first": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+      "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
+    },
+    "encodeurl": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz",
+      "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA="
+    },
+    "escape-html": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+      "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
+    },
+    "etag": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+      "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
+    },
+    "express": {
+      "version": "4.16.2",
+      "resolved": "https://registry.npmjs.org/express/-/express-4.16.2.tgz",
+      "integrity": "sha1-41xt/i1kt9ygpc1PIXgb4ymeB2w=",
+      "requires": {
+        "accepts": "1.3.4",
+        "array-flatten": "1.1.1",
+        "body-parser": "1.18.2",
+        "content-disposition": "0.5.2",
+        "content-type": "1.0.4",
+        "cookie": "0.3.1",
+        "cookie-signature": "1.0.6",
+        "debug": "2.6.9",
+        "depd": "1.1.1",
+        "encodeurl": "1.0.1",
+        "escape-html": "1.0.3",
+        "etag": "1.8.1",
+        "finalhandler": "1.1.0",
+        "fresh": "0.5.2",
+        "merge-descriptors": "1.0.1",
+        "methods": "1.1.2",
+        "on-finished": "2.3.0",
+        "parseurl": "1.3.2",
+        "path-to-regexp": "0.1.7",
+        "proxy-addr": "2.0.2",
+        "qs": "6.5.1",
+        "range-parser": "1.2.0",
+        "safe-buffer": "5.1.1",
+        "send": "0.16.1",
+        "serve-static": "1.13.1",
+        "setprototypeof": "1.1.0",
+        "statuses": "1.3.1",
+        "type-is": "1.6.15",
+        "utils-merge": "1.0.1",
+        "vary": "1.1.2"
+      }
+    },
+    "finalhandler": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz",
+      "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=",
+      "requires": {
+        "debug": "2.6.9",
+        "encodeurl": "1.0.1",
+        "escape-html": "1.0.3",
+        "on-finished": "2.3.0",
+        "parseurl": "1.3.2",
+        "statuses": "1.3.1",
+        "unpipe": "1.0.0"
+      }
+    },
+    "forwarded": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
+      "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
+    },
+    "fresh": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+      "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
+    },
+    "http-errors": {
+      "version": "1.6.2",
+      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz",
+      "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=",
+      "requires": {
+        "depd": "1.1.1",
+        "inherits": "2.0.3",
+        "setprototypeof": "1.0.3",
+        "statuses": "1.3.1"
+      },
+      "dependencies": {
+        "setprototypeof": {
+          "version": "1.0.3",
+          "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz",
+          "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ="
+        }
+      }
+    },
+    "iconv-lite": {
+      "version": "0.4.19",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz",
+      "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ=="
+    },
+    "inherits": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+      "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+    },
+    "ipaddr.js": {
+      "version": "1.5.2",
+      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.5.2.tgz",
+      "integrity": "sha1-1LUFvemUaYfM8PxY2QEP+WB+P6A="
+    },
+    "media-typer": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+      "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
+    },
+    "merge-descriptors": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+      "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
+    },
+    "methods": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+      "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
+    },
+    "mime": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
+      "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ=="
+    },
+    "mime-db": {
+      "version": "1.30.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz",
+      "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE="
+    },
+    "mime-types": {
+      "version": "2.1.17",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz",
+      "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=",
+      "requires": {
+        "mime-db": "1.30.0"
+      }
+    },
+    "ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+    },
+    "negotiator": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
+      "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
+    },
+    "on-finished": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+      "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
+      "requires": {
+        "ee-first": "1.1.1"
+      }
+    },
+    "parseurl": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
+      "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M="
+    },
+    "path-to-regexp": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+      "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
+    },
+    "proxy-addr": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.2.tgz",
+      "integrity": "sha1-ZXFQT0e7mI7IGAJT+F3X4UlSvew=",
+      "requires": {
+        "forwarded": "0.1.2",
+        "ipaddr.js": "1.5.2"
+      }
+    },
+    "qs": {
+      "version": "6.5.1",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz",
+      "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A=="
+    },
+    "range-parser": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
+      "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4="
+    },
+    "raw-body": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz",
+      "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=",
+      "requires": {
+        "bytes": "3.0.0",
+        "http-errors": "1.6.2",
+        "iconv-lite": "0.4.19",
+        "unpipe": "1.0.0"
+      }
+    },
+    "safe-buffer": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
+      "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
+    },
+    "send": {
+      "version": "0.16.1",
+      "resolved": "https://registry.npmjs.org/send/-/send-0.16.1.tgz",
+      "integrity": "sha512-ElCLJdJIKPk6ux/Hocwhk7NFHpI3pVm/IZOYWqUmoxcgeyM+MpxHHKhb8QmlJDX1pU6WrgaHBkVNm73Sv7uc2A==",
+      "requires": {
+        "debug": "2.6.9",
+        "depd": "1.1.1",
+        "destroy": "1.0.4",
+        "encodeurl": "1.0.1",
+        "escape-html": "1.0.3",
+        "etag": "1.8.1",
+        "fresh": "0.5.2",
+        "http-errors": "1.6.2",
+        "mime": "1.4.1",
+        "ms": "2.0.0",
+        "on-finished": "2.3.0",
+        "range-parser": "1.2.0",
+        "statuses": "1.3.1"
+      }
+    },
+    "serve-static": {
+      "version": "1.13.1",
+      "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.1.tgz",
+      "integrity": "sha512-hSMUZrsPa/I09VYFJwa627JJkNs0NrfL1Uzuup+GqHfToR2KcsXFymXSV90hoyw3M+msjFuQly+YzIH/q0MGlQ==",
+      "requires": {
+        "encodeurl": "1.0.1",
+        "escape-html": "1.0.3",
+        "parseurl": "1.3.2",
+        "send": "0.16.1"
+      }
+    },
+    "setprototypeof": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
+      "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
+    },
+    "statuses": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz",
+      "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4="
+    },
+    "type-is": {
+      "version": "1.6.15",
+      "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz",
+      "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=",
+      "requires": {
+        "media-typer": "0.3.0",
+        "mime-types": "2.1.17"
+      }
+    },
+    "unpipe": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+      "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
+    },
+    "utils-merge": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+      "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
+    },
+    "vary": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+      "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
+    }
+  }
+}

css/main.css → public/css/main.css


css/normalize.css → public/css/normalize.css


+ 90 - 0
public/css/styles.css

@@ -0,0 +1,90 @@
+body {
+	background: #f4f4f4;
+	font-family: Arial, Helvetica;
+}
+a {
+	text-decoration: none;
+}
+.navbar {
+	background: #444;
+  margin: 0 auto;
+	width: 600px;
+}
+.navbar ul {
+	margin: 20px 0 0 0;
+	padding-left: 0;
+}
+.navbar ul li {
+	display: inline-block;
+	padding: 10px 0;
+}
+.navbar ul li a {
+	color: #eee;
+	background: #444;
+	padding: 10px;
+}
+.container {
+  margin: 0px auto;
+	background: #fff;
+	border: 1px solid #eee;
+	padding: 30px;
+	width: 600px;
+	box-sizing: border-box;
+}
+.banner-container {
+  margin: 0px auto;
+	width: 600px;
+	border: none;
+}
+button {
+  padding: 10px;
+  color: white;
+  border: none;
+  border-radius: 3px;
+}
+.blue {
+  background: #24d;
+}
+.red {
+  background: #d42;
+}
+input {
+  border: 1px solid #ddd;
+  padding: 8px;
+  border-radius: 3px;
+}
+.product {
+	width: 48%;
+	margin: 1%;
+	border: 1px solid #ddd;
+	display: inline-block;
+	position: relative;
+	box-sizing: border-box;
+}
+.product img {
+	z-index: 5;
+	max-width: 100%;
+}
+.product .overlay {
+	position: absolute;
+	bottom: 0;
+	background: rgba(0, 0, 0, 0.7);
+	color: #ccc;
+	width: 100%;
+	text-align: center;
+	padding: 10px 0;
+}
+table.cart {
+	border-collapse: collapse;
+}
+table.cart tr {
+	width: 100%;
+}
+table.cart td.thumb img {
+	max-width: 90px;
+}
+table.cart th,
+table.cart td {
+	text-align: center;
+	border-bottom: 1px solid #ddd;
+}

+ 7 - 0
public/data/about.html

@@ -0,0 +1,7 @@
+<h1>About</h1>
+
+<h2>Who are we?</h2>
+<p>Nozama is an online store founded by our beloved CEO, John Doe.</p>
+
+<h2>What to you have in store?</h2>
+<p>We sell everything. EVERY. THING.</p>

+ 10 - 0
public/data/cart.json

@@ -0,0 +1,10 @@
+[
+  {
+    "productId": 1,
+    "quantity": 1
+  },
+  {
+    "productId": 6,
+    "quantity": 3
+  }
+]

+ 51 - 0
public/data/products.json

@@ -0,0 +1,51 @@
+[
+  {
+    "id": 1,
+    "name": "Jumelles",
+    "productRef": "tech-001",
+    "image": "binoculars.jpg",
+    "price": 99.0
+  },
+  {
+    "id": 2,
+    "name": "Lapin bébé",
+    "productRef": "childcare-001",
+    "image": "baby-bunny.jpg",
+    "price": 9.0
+  },
+  {
+    "id": 3,
+    "name": "Chargeur 1",
+    "productRef": "tech-002",
+    "image": "charger1.jpg",
+    "price": 12.0
+  },
+  {
+    "id": 4,
+    "name": "Chargeur 2",
+    "productRef": "tech-003",
+    "image": "charger2.jpg",
+    "price": 15.0
+  },
+  {
+    "id": 5,
+    "name": "Fausse caméra",
+    "productRef": "tech-004",
+    "image": "fake-camera.jpg",
+    "price": 59.0
+  },
+  {
+    "id": 6,
+    "name": "Masque à gaz M04",
+    "productRef": "tech-005",
+    "image": "m04-tactical-gas-mask.jpg",
+    "price": 419.50
+  },
+  {
+    "id": 7,
+    "name": "Samsung c5",
+    "productRef": "tech-006",
+    "image": "samsung-c5.jpg",
+    "price": 392.99
+  }
+]

img/.gitignore → public/images/.gitignore


BIN=BIN
public/images/products/baby-bunny.jpg


BIN=BIN
public/images/products/binoculars.jpg


BIN=BIN
public/images/products/charger1.jpg


BIN=BIN
public/images/products/charger2.jpg


BIN=BIN
public/images/products/fake-camera.jpg


BIN=BIN
public/images/products/m04-tactical-gas-mask.jpg


BIN=BIN
public/images/products/samsung-c5.jpg


+ 47 - 0
public/index.html

@@ -0,0 +1,47 @@
+<!doctype html>
+<html class="no-js" lang="">
+    <head>
+        <meta charset="utf-8">
+        <meta http-equiv="x-ua-compatible" content="ie=edge">
+        <title>Formation jQuery</title>
+        <meta name="description" content="">
+        <meta name="viewport" content="width=device-width, initial-scale=1">
+
+        <link rel="manifest" href="site.webmanifest">
+        <link rel="apple-touch-icon" href="icon.png">
+        <!-- Place favicon.ico in the root directory -->
+
+        <link rel="stylesheet" href="css/normalize.css">
+        <link rel="stylesheet" href="css/main.css">
+        <link rel="stylesheet" href="css/styles.css">
+    </head>
+    <body>
+        <!--[if lte IE 9]>
+            <p class="browserupgrade">You are using an <strong>outdated</strong> browser. Please <a href="https://browsehappy.com/">upgrade your browser</a> to improve your experience and security.</p>
+        <![endif]-->
+
+        <!-- Add your site or application content here -->
+        <div class="navbar">
+          <ul>
+            <li><a href="#welcome">Home</a></li>
+            <li><a href="#about">About</a></li>
+            <li><a href="#cart">Cart</a></li>
+          </ul>
+        </div>
+        <div class="banner-container">
+          <img src="http://via.placeholder.com/600x150" />
+        </div>
+        <div class="container" id="main">
+
+        </div>
+        <script src="js/vendor/modernizr-3.5.0.min.js"></script>
+        <!-- <script src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script> -->
+        <script src="js/vendor/jquery-3.2.1.min.js"></script>
+        <script src="js/vendor/lodash.min.js"></script>
+        <script src="js/plugins.js"></script>
+        <script src="js/main.js"></script>
+
+
+        <script src="js/app.js"></script>
+    </body>
+</html>

+ 132 - 0
public/js/app.js

@@ -0,0 +1,132 @@
+var baseUrl = '';
+
+
+(function($) {
+  $(document).ready(function() {
+    var $main = $('#main');
+    //
+    // function home() {
+    //   $('#main')
+    // }
+    //
+    // function showAlert(type, text) {
+    //   $('#alert')
+    //   .removeClass('success')
+    // }
+
+    var routeMap = {
+      about: {
+        data: function(cb) {
+          $.get(baseUrl + '/data/about.html', cb);
+        },
+        controller: function(aboutHtml) {
+          console.log('about', aboutHtml);
+          $('#main').html(aboutHtml);
+        }
+      },
+      welcome: {
+        data: function(cb) {
+          console.log('get data for welcome');
+          $.get(baseUrl + '/data/products.json', function(data) {
+            console.log('welcome.data.success', data);
+            cb(data);
+          }, 'json');
+        },
+        controller: function(products) {
+          console.log('controller', arguments, products);
+
+          products.forEach(product => {
+            $main.append('<div class="product">' +
+              '<img src="/images/products/' + product.image + '" alt="' + product.name + '" />' +
+              '<div class="overlay">' + product.name + '</div>' +
+            '</div>');
+          })
+        }
+      },
+      cart: {
+        data: function(cb) {
+          $.get(baseUrl + '/data/products.json', function(products) {
+            console.log('products', products);
+              $.get(baseUrl + '/data/cart.json', function(cart) {
+                console.log('cart', cart);
+                // console.log(cb.toString());
+                cb({products, cart});
+              });
+          });
+
+        },
+        controller: function(data) {
+          // console.log('controller', arguments, data);
+          // $('#main').html(data.products.length);
+          var products = data.products;
+          var cart = data.cart;
+          console.log(products);
+          var $table = $('<table class="cart"><tr>' +
+            '<th></th>' +
+            '<th>Product&nbsp;ref</th>' +
+            '<th>Product&nbsp;name</th>' +
+            '<th>Qty.</th>' +
+            '<th>Unit&nbsp;price</th>' +
+            '<th>Price</th>' +
+          '</tr></table>').appendTo($main);
+          var total = 0.0;
+          cart.forEach(function(item) {
+            console.log('cart item', item);
+            var product = _.find(products, {
+              id: item.productId
+            });
+            total += item.quantity * product.price;
+            $table.append('<tr>' +
+            '<td class="thumb"><img src="/images/products/' + product.image + '" alt="' + product.name + '" /></td>' +
+            '<td>' + product.productRef + '</td>' +
+            '<td>' + product.name + '</td>' +
+            '<td>' + item.quantity + '</td>' +
+            '<td>' + product.price + '€</td>' +
+            '<td>' + item.quantity * product.price + '€</td>' +
+            '</tr>'
+            )
+          });
+          $table.append('<tr><td colspan="5"></td>' +
+            '<td><strong>' +total + '€</strong></td></tr>'
+          );
+        }
+      }
+    };
+
+    function router(e) {
+      var hash = window.location.hash;
+      hash = hash ? hash.substr(1) : 'welcome';
+      console.log('hash changed', hash);
+      var existingRoutes = Object.keys(routeMap);
+      if(existingRoutes.indexOf(hash) === -1) {
+        console.log('error', 'route does not exist');
+      }
+
+      var routeHandler = routeMap[hash];
+      if(
+        typeof routeHandler.data !== 'function' ||
+        typeof routeHandler.controller !== 'function'
+      ) {
+          console.log('Error, route handler for "' + hash + '" is invalid', routeHandler);
+      }
+
+      routeHandler.data(function(data) {
+        // console.log('got data', hash, data);
+        $main.empty();
+        routeHandler.controller(data)
+      });
+    }
+
+
+
+
+    window.addEventListener('hashchange', router);
+    router();
+
+    // console.log('ok loading products...');
+    // $.get('/data/products.json?ts=' + Date.now(), function(data) {
+    //   alert('done');
+    //   // console.log('got products', data);
+    // }, 'json');
+  });
+})(jQuery);

js/main.js → public/js/main.js


js/plugins.js → public/js/plugins.js


js/vendor/jquery-3.2.1.min.js → public/js/vendor/jquery-3.2.1.min.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 136 - 0
public/js/vendor/lodash.min.js


js/vendor/modernizr-3.5.0.min.js → public/js/vendor/modernizr-3.5.0.min.js


+ 5 - 0
server.js

@@ -0,0 +1,5 @@
+const express = require('express');
+const app = express();
+
+app.use(express.static(__dirname + '/public'));
+app.listen(8080);