├── .gitignore ├── .vscode ├── extensions.json └── settings.json ├── LICENSE ├── LICENSE-CODE ├── README.md ├── assets ├── css-syntax.png ├── fabric.jpg ├── flux.png ├── scripts.js ├── shared.css ├── step.css ├── todo-components.png └── todo_screenshot.jpg ├── azure-pipelines.pr.yml ├── azure-pipelines.yml ├── bonus-jest ├── demo │ ├── README.md │ ├── index.html │ └── src │ │ ├── TestMe.spec.tsx │ │ ├── TestMe.tsx │ │ ├── index.spec.tsx │ │ ├── index.ts │ │ └── multiply.ts └── exercise │ ├── README.md │ ├── index.html │ └── src │ ├── TestMe.spec.tsx │ ├── TestMe.tsx │ ├── index.ts │ ├── stack.spec.ts │ └── stack.ts ├── bonus-servicecalls └── demo │ ├── README.md │ ├── index.html │ └── src │ ├── actions │ └── index.ts │ ├── components │ ├── TodoApp.tsx │ ├── TodoFooter.tsx │ ├── TodoHeader.tsx │ ├── TodoList.tsx │ └── TodoListItem.tsx │ ├── index.tsx │ ├── reducers │ └── index.ts │ ├── service │ └── index.ts │ └── store │ └── index.ts ├── index.html ├── jest.config.js ├── jest.setup.js ├── markdownReadme └── src │ └── index.ts ├── package-lock.json ├── package.json ├── playground └── index.html ├── prettier.config.js ├── server ├── index.js └── now.json ├── step1-01 ├── demo │ ├── index.html │ └── style.css ├── exercise │ ├── README.md │ ├── answers.html │ ├── baked_beans.jpg │ └── index.html └── lesson │ ├── README.md │ ├── index.html │ └── src │ └── index.tsx ├── step1-02 ├── demo │ └── index.html ├── exercise │ ├── README.md │ ├── answers.css │ └── index.html └── lesson │ ├── README.md │ ├── index.html │ └── src │ └── index.tsx ├── step1-03 ├── demo │ └── index.html ├── exercise │ ├── README.md │ ├── answer.js │ └── index.html └── lesson │ ├── README.md │ ├── index.html │ └── src │ └── index.tsx ├── step1-04 ├── demo │ └── index.html ├── final │ ├── README.md │ ├── index.html │ └── src │ │ ├── App.tsx │ │ ├── components │ │ ├── Button.css │ │ ├── Button.tsx │ │ └── Counter.tsx │ │ └── index.tsx └── lesson │ ├── README.md │ ├── index.html │ └── src │ └── index.tsx ├── step1-05 ├── TodoApp.html ├── demo │ ├── README.md │ ├── index.html │ └── src │ │ ├── App.tsx │ │ ├── index.tsx │ │ └── style.css └── exercise │ ├── README.md │ ├── index.html │ └── src │ ├── App.tsx │ ├── components │ ├── TodoHeader.tsx │ └── TodoListItem.tsx │ ├── index.tsx │ └── style.css ├── step1-06 ├── demo │ ├── README.md │ ├── index.html │ └── src │ │ ├── TodoApp.tsx │ │ ├── components │ │ ├── TodoFooter.tsx │ │ ├── TodoHeader.tsx │ │ ├── TodoList.tsx │ │ └── TodoListItem.tsx │ │ ├── index.tsx │ │ └── style.css ├── exercise │ ├── README.md │ ├── index.html │ └── src │ │ ├── TodoApp.tsx │ │ ├── components │ │ ├── TodoFooter.tsx │ │ ├── TodoHeader.tsx │ │ ├── TodoList.tsx │ │ └── TodoListItem.tsx │ │ ├── index.tsx │ │ └── style.css └── index.html ├── step1-07 ├── demo │ ├── README.md │ ├── index.html │ └── src │ │ ├── TodoApp.tsx │ │ ├── TodoApp.types.ts │ │ ├── components │ │ ├── TodoFooter.tsx │ │ ├── TodoHeader.tsx │ │ ├── TodoList.tsx │ │ └── TodoListItem.tsx │ │ ├── index.tsx │ │ └── style.css ├── exercise │ ├── README.md │ ├── index.html │ └── src │ │ ├── TodoApp.tsx │ │ ├── TodoApp.types.ts │ │ ├── components │ │ ├── TodoFooter.tsx │ │ ├── TodoHeader.tsx │ │ ├── TodoList.tsx │ │ └── TodoListItem.tsx │ │ ├── index.tsx │ │ └── style.css └── final │ ├── index.html │ └── src │ ├── TodoApp.tsx │ ├── TodoApp.types.ts │ ├── components │ ├── TodoFooter.tsx │ ├── TodoHeader.tsx │ ├── TodoList.tsx │ └── TodoListItem.tsx │ ├── index.tsx │ └── style.css ├── step2-01 ├── demo │ ├── README.md │ ├── index.html │ └── src │ │ ├── async │ │ └── index.ts │ │ ├── generics │ │ └── index.ts │ │ ├── index.tsx │ │ ├── interfaces │ │ └── index.ts │ │ ├── modules │ │ ├── default.ts │ │ ├── index.ts │ │ └── named.ts │ │ ├── spread │ │ └── index.ts │ │ └── types │ │ └── index.ts ├── exercise │ ├── README.md │ ├── index.html │ └── src │ │ ├── fibonacci.ts │ │ ├── index.ts │ │ └── stack.ts └── final │ ├── README.md │ ├── index.html │ └── src │ ├── fibonacci.ts │ ├── index.ts │ └── stack.ts ├── step2-02 ├── demo │ ├── README.md │ ├── index.html │ └── src │ │ ├── components │ │ ├── TodoApp.tsx │ │ ├── TodoFooter.tsx │ │ ├── TodoHeader.tsx │ │ ├── TodoList.tsx │ │ └── TodoListItem.tsx │ │ ├── index.tsx │ │ └── store │ │ └── index.ts └── exercise │ ├── README.md │ ├── index.html │ └── src │ ├── components │ ├── TodoApp.tsx │ ├── TodoFooter.tsx │ ├── TodoHeader.tsx │ ├── TodoList.tsx │ └── TodoListItem.tsx │ ├── index.tsx │ └── store │ └── index.ts ├── step2-03 ├── demo │ ├── README.md │ ├── index.html │ └── src │ │ ├── components │ │ ├── TodoApp.tsx │ │ ├── TodoFooter.tsx │ │ ├── TodoHeader.tsx │ │ ├── TodoList.tsx │ │ └── TodoListItem.tsx │ │ ├── index.tsx │ │ └── store │ │ └── index.ts └── exercise │ ├── README.md │ ├── index.html │ └── src │ ├── components │ ├── TodoApp.tsx │ ├── TodoFooter.tsx │ ├── TodoHeader.tsx │ ├── TodoList.tsx │ └── TodoListItem.tsx │ ├── index.tsx │ └── store │ └── index.ts ├── step2-04 ├── demo │ ├── README.md │ ├── index.html │ └── src │ │ ├── TodoContext.ts │ │ ├── components │ │ ├── TodoApp.tsx │ │ ├── TodoFooter.tsx │ │ ├── TodoHeader.tsx │ │ ├── TodoList.tsx │ │ └── TodoListItem.tsx │ │ ├── index.tsx │ │ └── store │ │ └── index.ts └── exercise │ ├── README.md │ ├── index.html │ └── src │ ├── TodoContext.ts │ ├── components │ ├── TodoApp.tsx │ ├── TodoFooter.tsx │ ├── TodoHeader.tsx │ ├── TodoList.tsx │ └── TodoListItem.tsx │ ├── index.tsx │ └── store │ └── index.ts ├── step2-05 ├── demo │ ├── README.md │ ├── index.html │ └── src │ │ ├── actions │ │ └── index.ts │ │ ├── index.tsx │ │ ├── reducers │ │ └── index.ts │ │ └── store │ │ └── index.ts └── exercise │ ├── README.md │ ├── index.html │ └── src │ ├── actions │ └── index.ts │ ├── index.tsx │ ├── reducers │ └── index.ts │ └── store │ └── index.ts ├── step2-06 ├── demo │ ├── README.md │ ├── index.html │ └── src │ │ ├── actions │ │ └── index.ts │ │ ├── components │ │ ├── TodoApp.tsx │ │ ├── TodoFooter.tsx │ │ ├── TodoHeader.tsx │ │ ├── TodoList.tsx │ │ └── TodoListItem.tsx │ │ ├── index.tsx │ │ ├── reducers │ │ └── index.ts │ │ └── store │ │ └── index.ts └── exercise │ ├── README.md │ ├── index.html │ └── src │ ├── actions │ └── index.ts │ ├── components │ ├── TodoApp.tsx │ ├── TodoFooter.tsx │ ├── TodoHeader.tsx │ ├── TodoList.tsx │ └── TodoListItem.tsx │ ├── index.tsx │ ├── reducers │ └── index.ts │ └── store │ └── index.ts ├── tsconfig.json └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | lib 4 | *.log 5 | .DS_Store 6 | tmp.json 7 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp 6 | "esbenp.prettier-vscode" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "prettier.printWidth": 140, 3 | "prettier.tabWidth": 2, 4 | "prettier.singleQuote": true, 5 | "editor.tabSize": 2, 6 | "editor.formatOnSave": true 7 | } 8 | -------------------------------------------------------------------------------- /LICENSE-CODE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-present Microsoft Corporation 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /assets/css-syntax.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/frontend-bootcamp/7cea32428e16b33a2ae4e54d2a87274a8171559b/assets/css-syntax.png -------------------------------------------------------------------------------- /assets/fabric.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/frontend-bootcamp/7cea32428e16b33a2ae4e54d2a87274a8171559b/assets/fabric.jpg -------------------------------------------------------------------------------- /assets/flux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/frontend-bootcamp/7cea32428e16b33a2ae4e54d2a87274a8171559b/assets/flux.png -------------------------------------------------------------------------------- /assets/scripts.js: -------------------------------------------------------------------------------- 1 | // prettier-ignore 2 | var appInsights = window.appInsights || function (a) { 3 | function b(a) { c[a] = function () { var b = arguments; c.queue.push(function () { c[a].apply(c, b) }) } } var c = { config: a }, d = document, e = window; setTimeout(function () { var b = d.createElement("script"); b.src = a.url || "https://az416426.vo.msecnd.net/scripts/a/ai.0.js", d.getElementsByTagName("script")[0].parentNode.appendChild(b) }); try { c.cookie = d.cookie } catch (a) { } c.queue = []; for (var f = ["Event", "Exception", "Metric", "PageView", "Trace", "Dependency"]; f.length;)b("track" + f.pop()); if (b("setAuthenticatedUserContext"), b("clearAuthenticatedUserContext"), b("startTrackEvent"), b("stopTrackEvent"), b("startTrackPage"), b("stopTrackPage"), b("flush"), !a.disableExceptionTracking) { f = "onerror", b("_" + f); var g = e[f]; e[f] = function (a, b, d, e, h) { var i = g && g(a, b, d, e, h); return !0 !== i && c["_" + f](a, b, d, e, h), i } } return c 4 | }({ 5 | instrumentationKey: "6ad37ae0-c4ab-4739-925c-1e2773c31f17" 6 | }); 7 | 8 | // prettier-ignore 9 | if (window.location.hostname !== 'localhost') { 10 | window.appInsights = appInsights, appInsights.queue && 0 === appInsights.queue.length && appInsights.trackPageView(null, null, { urlReferrer: document.referrer }); 11 | } 12 | -------------------------------------------------------------------------------- /assets/shared.css: -------------------------------------------------------------------------------- 1 | html { 2 | box-sizing: border-box; 3 | } 4 | *, 5 | *:before, 6 | *:after { 7 | box-sizing: inherit; 8 | } 9 | 10 | body { 11 | background-color: #f3f2f1; 12 | } 13 | 14 | a { 15 | color: #0078d4; 16 | text-decoration: none; 17 | } 18 | 19 | a:hover { 20 | text-decoration: underline; 21 | } 22 | 23 | h1 { 24 | margin: 0; 25 | } 26 | 27 | h1 a { 28 | font-size: 16px; 29 | font-weight: 400; 30 | } 31 | 32 | .Container { 33 | justify-content: center; 34 | padding: 20px 0; 35 | max-width: 1040px; 36 | margin: 0 auto; 37 | } 38 | 39 | .Tiles { 40 | grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); 41 | grid-gap: 20px; 42 | display: grid; 43 | list-style-type: none; 44 | margin: 0; 45 | padding: 0; 46 | counter-reset: steps; 47 | } 48 | 49 | .Tile { 50 | background-color: white; 51 | border-radius: 2px; 52 | box-shadow: 0 3.2px 7.2px 0 rgba(0, 0, 0, 0.132), 0 0.6px 1.8px 0 rgba(0, 0, 0, 0.108); 53 | opacity: 0.96; 54 | transition: all 0.15s linear; 55 | position: relative; 56 | overflow: hidden; 57 | padding: 12px; 58 | } 59 | 60 | .Tile:not(.Tile--intro):hover { 61 | box-shadow: 0 6.4px 14.4px 0 rgba(0, 0, 0, 0.132), 0 1.2px 3.6px 0 rgba(0, 0, 0, 0.108); 62 | opacity: 1; 63 | } 64 | 65 | .Tile.Tile--numbered::after { 66 | counter-increment: steps; 67 | content: counter(steps); 68 | position: absolute; 69 | left: -20px; 70 | bottom: -3px; 71 | font-size: 220px; 72 | line-height: 188px; 73 | color: #eaeaea; 74 | z-index: 1; 75 | } 76 | 77 | .Tile-link { 78 | align-items: center; 79 | text-align: center; 80 | color: #323130; 81 | display: flex; 82 | flex-direction: column; 83 | height: 148px; 84 | justify-content: center; 85 | text-decoration: none; 86 | position: relative; 87 | font-size: 24px; 88 | font-weight: 200; 89 | z-index: 2; 90 | } 91 | 92 | a.Tile-link { 93 | color: #0078d7; 94 | } 95 | 96 | a.Tile-link:hover { 97 | text-decoration: underline; 98 | } 99 | 100 | .Tile-link i { 101 | font-size: 32px; 102 | margin-bottom: 12px; 103 | color: #605e5c; 104 | } 105 | 106 | .Tile-links { 107 | font-size: 16px; 108 | color: #605e5c; 109 | } 110 | 111 | .Tile-links a { 112 | text-decoration: none; 113 | color: #0078d4; 114 | } 115 | 116 | .Tile-links a:hover { 117 | text-decoration: underline; 118 | } 119 | 120 | .Tile--intro { 121 | grid-column: span 2; 122 | padding: 20px; 123 | } 124 | 125 | .Tile--intro h1 { 126 | font-size: 24px; 127 | font-weight: 300; 128 | margin: 8px 0; 129 | padding: 0; 130 | } 131 | 132 | .Tile--intro p { 133 | font-size: 14px; 134 | margin: 0; 135 | } 136 | 137 | .Tile--intro a, 138 | .Tile--intro a:visited { 139 | color: #0078d4; 140 | } 141 | -------------------------------------------------------------------------------- /assets/todo-components.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/frontend-bootcamp/7cea32428e16b33a2ae4e54d2a87274a8171559b/assets/todo-components.png -------------------------------------------------------------------------------- /assets/todo_screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/frontend-bootcamp/7cea32428e16b33a2ae4e54d2a87274a8171559b/assets/todo_screenshot.jpg -------------------------------------------------------------------------------- /azure-pipelines.pr.yml: -------------------------------------------------------------------------------- 1 | # Node.js 2 | # Build a general Node.js project with npm. 3 | # Add steps that analyze code, save build artifacts, deploy, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/javascript 5 | 6 | trigger: none 7 | 8 | pool: 9 | vmImage: 'Ubuntu-16.04' 10 | 11 | steps: 12 | - task: NodeTool@0 13 | inputs: 14 | versionSpec: '10.x' 15 | displayName: 'Install Node.js' 16 | 17 | - script: | 18 | npm install 19 | npm run build 20 | displayName: 'npm install, build' 21 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | # Node.js 2 | # Build a general Node.js project with npm. 3 | # Add steps that analyze code, save build artifacts, deploy, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/javascript 5 | 6 | pr: none 7 | 8 | trigger: 9 | - master 10 | 11 | pool: 12 | vmImage: 'Ubuntu-16.04' 13 | 14 | steps: 15 | - task: NodeTool@0 16 | inputs: 17 | versionSpec: '10.x' 18 | displayName: 'Install Node.js' 19 | 20 | - script: | 21 | git config user.email "kchau@microsoft.com" 22 | git config user.name "Ken Chau" 23 | git remote set-url origin https://kenotron:$(git.pat)@github.com/Microsoft/frontend-bootcamp.git 24 | git checkout master 25 | git pull 26 | npm install 27 | git checkout -b build_$(Build.BuildId) 28 | npm run build 29 | git add . 30 | git commit -m "adding docs" 31 | git subtree split --prefix docs -b temp_$(Build.BuildId) 32 | git push origin temp_$(Build.BuildId):refs/heads/gh-pages --force 33 | displayName: 'npm install, build and push docs to gh-pages' 34 | -------------------------------------------------------------------------------- /bonus-jest/demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 |npm test11 | in the command line. 12 |
npm test11 | in the command line. 12 |
${highlighted}
`;
25 | };
26 | marked.setOptions({ renderer });
27 |
28 | if (div) {
29 | const response = await fetch(div.dataset['src'] || '../README.md');
30 | const markdownText = await response.text();
31 | div.innerHTML = marked(markdownText);
32 | restoreScroll(div);
33 |
34 | div.addEventListener('scroll', evt => {
35 | saveScroll(div);
36 | });
37 |
38 | window.addEventListener('resize', evt => {
39 | saveScroll(div);
40 | });
41 | }
42 | }
43 |
44 | const scrollKey = `${window.location.pathname}_scrolltop`;
45 |
46 | function saveScroll(div: HTMLElement) {
47 | window.localStorage.setItem(scrollKey, String(div.scrollTop));
48 | }
49 |
50 | function restoreScroll(div: HTMLElement) {
51 | const scrollTop = window.localStorage.getItem(scrollKey);
52 | if (scrollTop) {
53 | div.scrollTop = parseInt(scrollTop);
54 | }
55 | }
56 |
57 | run();
58 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bootcamp",
3 | "version": "1.0.0",
4 | "description": "",
5 | "repository": {
6 | "type": "git",
7 | "url": "https://github.com/Microsoft/frontend-bootcamp"
8 | },
9 | "main": "index.js",
10 | "scripts": {
11 | "start:client": "webpack-dev-server --mode development --progress --open",
12 | "test": "jest --watch",
13 | "build": "rimraf docs && webpack --progress --mode production",
14 | "start:server": "nodemon -w server server/index.js",
15 | "start": "run-p start:server start:client",
16 | "deploy-ghpages": "git push origin :gh-pages && git subtree push --prefix docs origin gh-pages"
17 | },
18 | "keywords": [],
19 | "author": "",
20 | "license": "ISC",
21 | "devDependencies": {
22 | "@types/body-parser": "^1.17.0",
23 | "@types/cors": "^2.8.4",
24 | "@types/enzyme": "^3.9.0",
25 | "@types/express": "^4.16.1",
26 | "@types/jest": "^23.3.13",
27 | "@types/node": "~10.12.21",
28 | "@types/react": "^16.7.20",
29 | "@types/react-dom": "^16.0.11",
30 | "@types/react-redux": "^7.0.0",
31 | "@types/redux": "^3.6.0",
32 | "@types/uuid": "^3.4.4",
33 | "body-parser": "^1.18.3",
34 | "copy-webpack-plugin": "^4.6.0",
35 | "cors": "^2.8.5",
36 | "css-loader": "^2.1.0",
37 | "fork-ts-checker-async-overlay-webpack-plugin": "^0.1.0",
38 | "fork-ts-checker-webpack-plugin": "^0.5.2",
39 | "html-webpack-plugin": "^4.0.0-beta.5",
40 | "jest": "^24.1.0",
41 | "nodemon": "^1.18.9",
42 | "npm-run-all": "^4.1.5",
43 | "rimraf": "^2.6.3",
44 | "style-loader": "^0.23.1",
45 | "ts-jest": "^24.0.0",
46 | "ts-loader": "^5.3.3",
47 | "tslint": "^5.13.0",
48 | "typescript": "^3.3.3",
49 | "uuid": "^3.3.2",
50 | "webpack": "^4.28.4",
51 | "webpack-cli": "^3.2.1",
52 | "webpack-dev-server": "^3.1.14"
53 | },
54 | "dependencies": {
55 | "@uifabric/fluent-theme": "^0.14.1",
56 | "@uifabric/theme-samples": "^0.1.4",
57 | "enzyme": "^3.9.0",
58 | "enzyme-adapter-react-16": "^1.9.1",
59 | "express": "^4.16.4",
60 | "highlight.js": "^9.14.2",
61 | "immer": "^1.12.1",
62 | "marked": "^0.6.1",
63 | "office-ui-fabric-react": "^6.144.0",
64 | "react": "^16.8.3",
65 | "react-dom": "^16.8.3",
66 | "react-redux": "^6.0.0",
67 | "redux": "^4.0.1",
68 | "redux-devtools-extension": "^2.13.8",
69 | "redux-starter-kit": "^0.4.3",
70 | "redux-thunk": "^2.3.0"
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/playground/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
13 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/prettier.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | singleQuote: true,
3 | tabWidth: 2,
4 | printWidth: 140
5 | };
6 |
--------------------------------------------------------------------------------
/server/index.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | const fs = require('fs');
3 | const express = require('express');
4 | const bodyParser = require('body-parser');
5 | const cors = require('cors');
6 | const app = express();
7 |
8 | const store = {
9 | /** @type {any} */
10 | read() {
11 | if (fs.existsSync('tmp.json')) {
12 | store.todos = JSON.parse(fs.readFileSync('tmp.json').toString());
13 | } else {
14 | store.todos = {};
15 | }
16 |
17 | return store.todos;
18 | },
19 |
20 | save() {
21 | fs.writeFileSync('tmp.json', JSON.stringify(store.todos));
22 | },
23 |
24 | todos: {}
25 | };
26 |
27 | app.use(bodyParser.json());
28 | app.use(cors());
29 |
30 | app.get('/todos', (req, res) => {
31 | res.json(store.read());
32 | });
33 |
34 | app.put('/todos/:id', (req, res) => {
35 | store.todos[req.params.id] = req.body;
36 | store.save();
37 | res.json('ok');
38 | });
39 |
40 | app.post('/todos/:id', (req, res) => {
41 | store.todos[req.params.id] = req.body;
42 | store.save();
43 | res.json('ok');
44 | });
45 |
46 | app.delete('/todos/:id', (req, res) => {
47 | delete store.todos[req.params.id];
48 | store.save();
49 | res.json('ok');
50 | });
51 |
52 | app.post('/todos', (req, res) => {
53 | store.todos = req.body;
54 | store.save();
55 | res.json('ok');
56 | });
57 |
58 | app.get('/hello', (req, res) => {
59 | res.send('world');
60 | });
61 |
62 | app.listen(process.env.NODE_ENV === 'production' ? undefined : 3000, () => {
63 | console.log('Listening at http://localhost:3000');
64 | });
65 |
--------------------------------------------------------------------------------
/server/now.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 2,
3 | "name": "todo-server",
4 | "builds": [{ "src": "*.js", "use": "@now/node-server" }],
5 | "env": {
6 | "NODE_ENV": "production"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/step1-01/demo/style.css:
--------------------------------------------------------------------------------
1 | aside {
2 | float: right;
3 | width: 33%;
4 | padding: 10px;
5 | background: #eee;
6 | }
7 |
8 | form > div {
9 | margin-bottom: 20px;
10 | }
11 |
12 | h2 a {
13 | color: #0078d4;
14 | text-decoration: none;
15 | }
16 |
17 | h2 a:hover {
18 | text-decoration: underline;
19 | }
20 |
21 | body {
22 | font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
23 | }
24 |
--------------------------------------------------------------------------------
/step1-01/exercise/README.md:
--------------------------------------------------------------------------------
1 | # Step 1.1 - Introduction to HTML (Exercise)
2 |
3 | See index.html from [npm start](http://localhost:8080/step1-01/exercise/) or the [live site](https://microsoft.github.io/frontend-bootcamp/step1-01/exercise/) for the exercise.
--------------------------------------------------------------------------------
/step1-01/exercise/answers.html:
--------------------------------------------------------------------------------
1 | It's great how a single meal can take you back dozens of years. This is one of those recipes that never seems to fail to impress.
3 |I learned this recipe from the cousin of one of my college friends back in Nashville Tennessee. We had an amazing 4th of July feast which included this recipe and some bratwurst like these Wisconsin Beer Brats
4 | 5 |Burning off most of the liquid gives you nice, hearty, sticky beans. If the beans get too dry, you can always add beer!
39 | 40 |13 | /* 14 | Step 1 Exercise 15 | 16 | The power of HTML is its ability to represent complex information in a way that conveys meaning. In this exercise you are going to be creating an HTML page for my favorite recipe. 17 | 18 | ## The Exercise 19 | 20 | 1. Create a recipe page to host our recipe 21 | 2. Use header, main, footer, headings (h1/h2 etc), paragraphs, lists 22 | 3. Use ordered and unordered lists appropriately 23 | 4. Add the `baked_beans.jpg` image: https://raw.githubusercontent.com/Microsoft/frontend-bootcamp/master/step1-01/exercise/baked_beans.jpg 24 | 5. Add an anchor tag around 'Wisconsin Beer Brats' 25 | 26 | > Note that CodePen takes care of the `HTML` and `Body` tags, so you can simply start with the content 27 | 28 | ## The Recipe 29 | 30 | Title: 31 | 4th of July Baked Beans 32 | 33 | Description: 34 | It's great how a single meal can take you back dozens of years. This is one of those recipes that never seems to fail to impress. 35 | 36 | I learned this recipe from the cousin of one of my college friends back in Nashville Tennessee. We had an amazing 4th of July feast which included this recipe and some bratwurst like these Wisconsin Beer Brats https://www.culinaryhill.com/wisconsin-beer-brats/ 37 | 38 | Prep Time: 10 minutes 39 | Cook time: 3+ hours 40 | Servings: 12 41 | 42 | Ingredients: 43 | 1LB Bacon chopped 44 | 3 Cans Bush's Original Baked Beans 45 | 1 Walla Wall Onion chopped 46 | 3 ground garlic cloves 47 | 4 Tablespoons of mustard 48 | 3 Tablespoons of molasses 49 | 4 Tablespoons of brown sugar 50 | 51 | Directions: 52 | Cook bacon until it is mostly cooked, then drain most of the grease and put aside 53 | Cook onion in remaining bacon grease 54 | Combine onions and bacon, then add garlic, cook for a few more minutes 55 | Add beans and get up to simmer temperature 56 | Add mustard until your beans are nice and yellow 57 | Add molasses until color darkens again 58 | Add brown sugar until properly sweet 59 | Simmer for a long time, occasionally stirring 60 | 61 | Expert Tips: 62 | Burning off most of the liquid gives you nice, hearty, sticky beans. 63 | If the beans get too dry, you can always add beer! 64 | 65 | Nutritional Information: 66 | Calories: lots 67 | Fat: lots 68 | Fun: lots 69 | 70 | */ 71 | 72 |73 |
Add Recipe Here74 |
13 | /* 1. */ 14 | 15 | /* 2. */ 16 | 17 | /* 3. */ 18 | 19 | /* 4. */ 20 | 21 | /* 5. */ 22 | 23 | /* 6. */ 24 | 25 | /* Bonus */ 26 |27 |
28 | <!-- Without changing the HTML markup, apply the styles asked for in the markup. Do not apply styles that a tag doesn't ask for. --> 29 | 30 | <section> 31 | <h2>1. Text Color: Red</h2> 32 | <div>2. Color Green (hint: Sibling Selector)</div> 33 | <main> 34 | <ul class="myList"> 35 | <li> 36 | 3. Border Green 37 | </li> 38 | </ul> 39 | <div class="myClass">4. Background Green</div> 40 | <div class="myClass otherClass"> 41 | 5. Background Green & Color White 42 | (Hint Qualified Selector) 43 | </div> 44 | <div id="myId" class="otherClass">6. Background Yellow</div> 45 | </main> 46 | <ul> 47 | <li> 48 | Don't Style Me 49 | </li> 50 | </ul> 51 | <div>Bonus: Border Pink</div> 52 | </section> 53 |54 |