Skip to content

Commit 5a105f8

Browse files
committed
02_03
0 parents  commit 5a105f8

File tree

150 files changed

+10434
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

150 files changed

+10434
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.DS_Store

Ch01/01_01/index.html

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<title>Professional Inventory System</title>
6+
<link rel="stylesheet" href="src/app.css" />
7+
<meta
8+
name="viewport"
9+
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"
10+
/>
11+
</head>
12+
<body>
13+
<div id="app"></div>
14+
<div id="error-dialog"></div>
15+
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
16+
<script src="https://cdn.jsdelivr.net/npm/jquery@3.4.0/dist/jquery.min.js"></script>
17+
<script src="src/app.js"></script>
18+
</body>
19+
</html>

Ch01/01_01/src/Storage.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* Retrieves data from storage
3+
*/
4+
function getFromStorage(key) {
5+
return __delay(() => {
6+
const serialized = localStorage.getItem(key) || "";
7+
return JSON.parse(serialized);
8+
});
9+
}
10+
11+
/**
12+
* Save data to storage
13+
*/
14+
function saveToStorage(key, data) {
15+
return __delay(() => {
16+
const serialized = JSON.stringify(data);
17+
localStorage.setItem(key, serialized);
18+
});
19+
}
20+
21+
/** helper function to introduce a random delay to simulate network conditions */
22+
function __delay(action) {
23+
return new Promise(done => {
24+
setTimeout(
25+
() => done(action()),
26+
Math.random() * 2000 // delay from 0-2 seconds
27+
);
28+
});
29+
}

Ch01/01_01/src/app.css

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
@import url("https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css");
2+
@import url("https://cdn.jsdelivr.net/gh/FortAwesome/Font-Awesome@5.8.1/css/all.min.css");
3+
@import url("pages/addItem.css");
4+
@import url("pages/inventory.css");
5+
6+
body {
7+
width: 80%;
8+
margin: auto;
9+
}
10+
11+
.flex {
12+
display: flex;
13+
}
14+
.grow {
15+
flex-grow: 1;
16+
}
17+
18+
.title {
19+
margin-bottom: 1em;
20+
}
21+
22+
.loading {
23+
font-size: 150%;
24+
padding-top: 40%;
25+
text-align: center;
26+
}

Ch01/01_01/src/app.js

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
const basePath = "src"
2+
3+
const loadDependencies = () => Promise.all([
4+
$.get(`${basePath}/testData.js`),
5+
$.get(`${basePath}/storage.js`),
6+
$.get(`${basePath}/inventoryStore.js`)
7+
]);
8+
9+
const loadPages = () => Promise.all([
10+
$.get(`${basePath}/pages/addItem.js`),
11+
$.get(`${basePath}/pages/inventory.js`)
12+
]);
13+
14+
Vue.component("loading", {
15+
template: `
16+
<div class="loading">
17+
<strong>Loading...</strong>
18+
<div class="progress">
19+
<div class="progress-bar progress-bar-striped active" role="progressbar" aria-valuenow="45" aria-valuemin="0" aria-valuemax="100" style="width: 100%">
20+
<span class="sr-only">Loading...</span>
21+
</div>
22+
</div>
23+
</div>
24+
`
25+
});
26+
27+
Vue.component("app", () => ({
28+
delay: 200,
29+
loading: { template: `<loading />` },
30+
component:
31+
loadDependencies()
32+
.then(() => inventoryStore.isInitialized)
33+
.then(loadPages)
34+
.then(() => ({
35+
data: () => ({
36+
inventoryStore: null,
37+
currentRoute: null,
38+
CurrentPage: null,
39+
routes: {
40+
"add-item": addItemPage,
41+
"inventory": inventoryPage
42+
}
43+
}),
44+
methods: {
45+
syncRoute() {
46+
this.currentRoute = window.location.hash.replace(/^#\//, "");
47+
}
48+
},
49+
watch: {
50+
currentRoute() {
51+
const page = this.routes[this.currentRoute];
52+
this.CurrentPage = page || inventoryPage;
53+
}
54+
},
55+
created() {
56+
this.inventoryStore = inventoryStore;
57+
window.addEventListener("hashchange", this.syncRoute);
58+
this.syncRoute();
59+
},
60+
template: `
61+
<div class="container-fluid">
62+
<div class="header clearfix">
63+
<h3 class="text-muted">Inventory Management System</h3>
64+
</div>
65+
<component :is="CurrentPage"></component>
66+
</div>`
67+
}))
68+
}));
69+
70+
new Vue({ el: "#app", template: `<app/>` });

Ch01/01_01/src/inventoryStore.js

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
class InventoryStore {
2+
/** the inventory categories */
3+
get categories() {
4+
return this._categories;
5+
}
6+
7+
/** the inventory items */
8+
get items() {
9+
return this._items;
10+
}
11+
12+
/** promise indicating whether the store has been initialized */
13+
get isInitialized() {
14+
return this._isInitialized;
15+
}
16+
17+
constructor() {
18+
// define and initialize properties (which happen to be "private")
19+
this._categories = [];
20+
this._items = [];
21+
22+
// load initial set of data
23+
this._isInitialized = this._load();
24+
}
25+
26+
/**
27+
* Locates a specific item from inventory
28+
*
29+
* @param {string} trackingNumber the item's tracking number
30+
* @returns the inventory item with the given tracking number, or null
31+
*/
32+
getItem(trackingNumber) {
33+
return this._items.find(x => x.trackingNumber === trackingNumber);
34+
}
35+
36+
/**
37+
* Adds an item to inventory
38+
*
39+
* @param {InventoryItem} item the item to add to inventory
40+
* @returns {Promise<InventoryItem>} promise containing the updated item after it's been saved
41+
*/
42+
addItem(item) {
43+
const errors = this.validateItem(item);
44+
45+
if (errors.length) {
46+
return Promise.reject(errors);
47+
}
48+
49+
const trackingNumber = Math.random()
50+
.toString(36)
51+
.substr(2, 9);
52+
53+
item.trackingNumber = trackingNumber;
54+
55+
this._items.push(item);
56+
57+
return this._save().then(() => item);
58+
}
59+
60+
/**
61+
* validate an inventory item
62+
*
63+
* @param {InventoryItem} item the inventory item to validate
64+
* @returns {ValidationError[]} an array of validation errors
65+
*/
66+
validateItem(item) {
67+
let errors = [];
68+
69+
function addError(field, message) {
70+
errors.push({ field, message });
71+
}
72+
73+
//#region Validation logic applying to any/all types of inventory items
74+
75+
if (item == null) {
76+
addError("", "item is null");
77+
return errors;
78+
}
79+
80+
if (!item.type) {
81+
addError("type", "Please select a valid Category");
82+
}
83+
84+
if (!item.name) {
85+
addError("name", "Name must be greater then 5 characters long");
86+
}
87+
88+
if (!item.assignedTo) {
89+
addError("assignedTo", "Please select the person this is assigned to");
90+
}
91+
92+
if (!item.subCategory) {
93+
addError("assignedTo", "Please select a Sub-Category");
94+
}
95+
96+
//#endregion
97+
98+
switch (item.type) {
99+
// Computer-specific validation
100+
case "computer":
101+
if (item.year > new Date().getFullYear()) {
102+
addError("name", "Please select a year (future years are not valid)");
103+
}
104+
105+
if (!item.serialNumber) {
106+
addError("serialNumber", "Please specify a valid serial number");
107+
}
108+
break;
109+
110+
// Furniture-specific validation
111+
case "furniture":
112+
if (!item.model) {
113+
addError(
114+
"model",
115+
"Please provide a model, serial number, or description"
116+
);
117+
}
118+
119+
if (!item.manufacturer) {
120+
addError("manufacturer", "Please identify the item's manufacturer");
121+
}
122+
break;
123+
}
124+
125+
return errors;
126+
}
127+
128+
/**
129+
* Removes an item from inventory
130+
*
131+
* @param {InventoryItem} item the item to remove from inventory
132+
* @returns {Promise<void>} a promise which resolves once the task is complete
133+
*
134+
*/
135+
removeItem(item) {
136+
this._items.splice(this._items.findIndex(item), 1);
137+
return this._save();
138+
}
139+
140+
//#region Protected methods
141+
142+
/* NOTE:
143+
* This demo uses local storage to save and load inventory items,
144+
* but in a real app these would be AJAX calls to a server.
145+
*/
146+
147+
/**
148+
* Load the current inventory items.
149+
*
150+
* @returns {Promise<boolean>} a promise with the loading state
151+
*
152+
* @private <-- just information, doesn't actually do anything at runtime
153+
*/
154+
_load() {
155+
return Promise.all([
156+
getFromStorage("Categories"),
157+
getFromStorage("Inventory")
158+
]).then(([categories, items]) => {
159+
this._categories = categories;
160+
this._items = items;
161+
});
162+
}
163+
164+
/**
165+
* Save the inventory items to the data source
166+
*
167+
* @returns {Promise<void>} a promise which resolves once the task is complete
168+
*
169+
* @private <-- just information, doesn't actually do anything at runtime
170+
*/
171+
_save() {
172+
return saveToStorage("Inventory", this._items);
173+
}
174+
175+
//#endregion
176+
}
177+
178+
// Create a "static" singleton instance for the entire application to use
179+
InventoryStore.instance = new InventoryStore();
180+
181+
// Expose the singleton in its own variable
182+
const inventoryStore = InventoryStore.instance;

Ch01/01_01/src/model.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
let displayName: string = "Jess's standing desk";
2+
let inventoryType: string = "furniture";
3+
let trackingNumber: string = "FD123455";
4+
let createDate: Date = new Date();
5+
let originalCost = 425;
6+
7+
interface InventoryItem {
8+
displayName: string;
9+
inventoryType: string;
10+
readonly trackingNumber: string;
11+
createDate: Date;
12+
originalCost?: number;
13+
14+
addNote?: (note: string) => string;
15+
}
16+
17+
function getInventoryItem(trackingNumber: string): InventoryItem {
18+
return null;
19+
}
20+
21+
function saveInventoryItem(item: InventoryItem) {
22+
}
23+
24+
let inventoryItem = getInventoryItem(trackingNumber);
25+
26+
let updatedInventoryItem = inventoryItem;
27+
28+
inventoryItem.createDate = new Date();
29+
30+
saveInventoryItem({
31+
displayName: "MacBook Pro 15 Retina",
32+
inventoryType: "computer",
33+
trackingNumber: "MBP123456",
34+
createDate: new Date(),
35+
});

Ch01/01_01/src/pages/addItem.css

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
.form-container {
2+
position: relative;
3+
}
4+
5+
.form-container form {
6+
padding: 2em;
7+
}
8+
9+
.saving {
10+
position: absolute;
11+
z-index: 1000;
12+
display: flex;
13+
background-color: rgba(200, 200, 200, 0.6);
14+
width: 100%;
15+
height: 100%;
16+
align-items: center;
17+
justify-content: center;
18+
font-size: 200%;
19+
font-weight: bold;
20+
font-style: italic;
21+
border-radius: 10px;
22+
}

0 commit comments

Comments
 (0)