Skip to content

Commit 2a02686

Browse files
committed
finished script
1 parent c50e454 commit 2a02686

File tree

1 file changed

+330
-0
lines changed
  • Zua - Full-Stack Web Application/media

1 file changed

+330
-0
lines changed

Zua - Full-Stack Web Application/media/script.md

Lines changed: 330 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,333 @@ Hello everyone, Zua here, today I will be explaining how to build a full-stack w
1111
## Explanation (Intermissions)
1212

1313
1. Now that the backend is complete, let's review what we just created. Our `express` API webserver listens for requests from clients. For testing purposes, our client right now is `postman`. The Postman client will initiate a request to the API webserver we just created. The API will then send us back a response based on what the user initially requested. Hopefully now, it's all starting to come together. Now, let's build a user-friendly interface that allows us to interact with our new API.
14+
15+
___
16+
17+
## Procedure
18+
19+
### Frontend:
20+
21+
* Begin Design UI - `index.html`
22+
```html
23+
<!doctype html>
24+
<html lang="en">
25+
<head>
26+
<meta charset="utf-8">
27+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
28+
<meta name="viewport" content="width=device-width, initial-scale=1">
29+
<title>Messenger App</title>
30+
<style>
31+
#footer {
32+
position: fixed;
33+
left: 0;
34+
bottom: 0;
35+
width: 100%;
36+
}
37+
</style>
38+
<!-- Bootstrap CSS Stylesheet-->
39+
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"
40+
integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
41+
<!-- Bootstrap Javascript-->
42+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"
43+
integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p"
44+
crossorigin="anonymous"></script>
45+
<!-- Axios HTTP Library -->
46+
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
47+
</head>
48+
49+
<body>
50+
<div class="container">
51+
<!-- Title header -->
52+
<h1>Messenger App</h1>
53+
<hr />
54+
<!-- Bootstrap Accordion https://getbootstrap.com/docs/5.0/components/accordion START -->
55+
<div class="accordion" id="accordionExample" style="margin-bottom: 25px">
56+
<div class="accordion-item">
57+
<h2 class="accordion-header" id="headingOne">
58+
<button class="accordion-button" type="button" data-bs-toggle="collapse"
59+
data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
60+
Authors
61+
</button>
62+
</h2>
63+
<div id="collapseOne" class="accordion-collapse collapse show" aria-labelledby="headingOne"
64+
data-bs-parent="#accordionExample">
65+
<div id="authors" class="accordion-body">
66+
</div>
67+
</div>
68+
</div>
69+
</div>
70+
<!-- Bootstrap Accordion END -->
71+
<!-- Card container (Houses all cards/messages) START -->
72+
<div class="row" id="cards" style="margin-bottom: 50px">
73+
</div>
74+
<!-- Footer (sticks to the bottom of the screen) -->
75+
<div id="footer">
76+
<button style="width: 100%" class="btn btn-primary">Create</button>
77+
</div>
78+
79+
</div>
80+
<!-- External custom scripts -->
81+
<script src="API.js"></script>
82+
<script src="utils.js"></script>
83+
<script src="actions.js"></script>
84+
<script>
85+
// More code will be here
86+
</script>
87+
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
88+
<script src="https://code.jquery.com/jquery-1.12.4.min.js"
89+
integrity="sha384-nvAa0+6Qg9clwYCGGPpDQLVpLNn0fRaROjHqs13t4Ggj3Ez50XnGQqc/r8MhnRDZ"
90+
crossorigin="anonymous"></script>
91+
</body>
92+
</html>
93+
```
94+
____
95+
96+
* Write API Wrapper - `API.js`
97+
```js
98+
var API = { // this object acts as our API Wrapper. It works standalone! (with the exception of the axios lib)
99+
api: axios.create({ baseURL: "http://localhost:5000/api" }),
100+
getMessage: function (id) {
101+
return this.api.get(`/message/${id}`)
102+
.then(response => response.data)
103+
},
104+
getAllMessages: function () {
105+
return this.api.get("/message/")
106+
.then(response => response.data)
107+
},
108+
editMessage: function (author, content, id) {
109+
return this.api.put(`/message/${id}?author=${author}`,
110+
new URLSearchParams({ content }))
111+
.then(response => response.data)
112+
},
113+
deleteMessage: function (author, id) {
114+
return this.api.delete(`/message/${id}?author=${author}`)
115+
.then(response => response.data)
116+
},
117+
createMessage: function (author, content) {
118+
return this.api.post(`/message/create?author=${author}`,
119+
new URLSearchParams({ content }))
120+
.then(response => response.data)
121+
},
122+
getAllAuthors: function () {
123+
return this.api.get(`/author/`)
124+
.then(response => response.data)
125+
},
126+
getMessagesByAuthor: function (author) {
127+
return this.api.get(`/author/${author}`)
128+
.then(response => response.data)
129+
},
130+
};
131+
```
132+
133+
___
134+
135+
* Start utility helper script - `utils.js`
136+
```js
137+
var utils = {
138+
/**
139+
* @description convert an object to a meaningful output that users can read
140+
* @param {Object} o - array of prompts to ask the user
141+
* @returns {String} easy to understand string output
142+
*/
143+
objToString: (o) => {
144+
let final = ''
145+
for (const [key, value] of Object.entries(o)) {
146+
final += `${key}: ${value}\n`
147+
}
148+
return final
149+
},
150+
/**
151+
* @description prompt the user with multiple prompts
152+
* @param {Array} prompts - array of prompts to ask the user
153+
* @returns {Array} list of the user's responses
154+
*/
155+
promptUser: (prompts) => prompts.map(p => prompt(p))
156+
};
157+
```
158+
159+
___
160+
161+
* Write actions script - `actions.js`
162+
163+
```js
164+
// actions get executed based on user intractability
165+
var actions = {
166+
createMessage: function () { // gets executed when the create message button gets pressed
167+
const inputs = utils.promptUser(["Name: ", "Message: "])
168+
if (inputs.every(e => e)) { // checks if all fields were filled in with some sort of content
169+
API.createMessage(...inputs).then(response => {
170+
alert("Created message successfully")
171+
this.refreshMessages()
172+
}).catch(alert)
173+
} else {
174+
alert("Please enter the fields correctly")
175+
}
176+
},
177+
refreshMessages: function (author) { // updates ui with all current messages & authors (if no author provided)
178+
if (!author) {
179+
API.getAllAuthors().then((response) => {
180+
document.querySelector("#authors").innerHTML =
181+
"Filter by: <button class='btn btn-primary' onclick='actions.refreshMessages()'>Everyone</button>&nbsp;"
182+
response.map(author => {
183+
document.querySelector("#authors").innerHTML +=
184+
`<button class="btn btn-secondary" onclick='actions.refreshMessages("${author}")'>${author}</button>&nbsp;`
185+
})
186+
}).catch(alert)
187+
}
188+
API[author ? "getMessagesByAuthor" : "getAllMessages"](author).then(response => {
189+
document.querySelector("#cards").innerHTML = "" // reset before appending
190+
response.forEach(msg => {
191+
document.querySelector("#cards").innerHTML += utils.card(msg)
192+
})
193+
}).catch(alert)
194+
},
195+
moreInfo: function (id) {
196+
API.getMessage(id).then((data) => { alert(utils.objToString(data)) })
197+
.catch(alert)
198+
},
199+
editMessage: function (id) {
200+
API.editMessage(...utils.promptUser(["Name: ", "Edited message: "]), id) // spread syntax
201+
.then((response) => {
202+
this.refreshMessages()
203+
alert("Edited message.")
204+
}).catch(alert)
205+
},
206+
deleteMessage: function (id) {
207+
API.deleteMessage(...utils.promptUser(["Name: "]), id)
208+
.then((response) => {
209+
this.refreshMessages()
210+
alert("Deleted message.")
211+
}).catch(alert)
212+
}
213+
};
214+
```
215+
216+
___
217+
218+
* Add render card function - `utils.js`
219+
```diff
220+
var utils = {
221+
/**
222+
* @description convert an object to a meaningful output that users can read
223+
* @param {Object} o - array of prompts to ask the user
224+
* @returns {String} easy to understand string output
225+
*/
226+
objToString: (o) => {
227+
let final = ''
228+
for (const [key, value] of Object.entries(o)) {
229+
final += `${key}: ${value}\n`
230+
}
231+
return final
232+
},
233+
/**
234+
* @description prompt the user with multiple prompts
235+
* @param {Array} prompts - array of prompts to ask the user
236+
* @returns {Array} list of the user's responses
237+
*/
238+
promptUser: (prompts) => prompts.map(p => prompt(p)),
239+
+ card: ({author, content, timestamp, edited, id}) => {
240+
+ return `
241+
+ <div class="col">
242+
+ <div class="card">
243+
+ <div class="card-body">
244+
+ <h3 class="card-title"><b>${author}</b></h5>
245+
+ <p class="card-text fs-2">${content}</p>
246+
+ <p class="text-secondary fs-6">${timestamp} ${edited ? "<i>(edited)</i>" : ''}</p>
247+
+ <button onclick="actions.moreInfo('${id}')" class="btn btn-info">More Info</button>
248+
+ <button onclick="actions.editMessage('${id}')" class="btn btn-warning">
249+
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-pencil-square" viewBox="0 0 16 16">
250+
+ <path d="M15.502 1.94a.5.5 0 0 1 0 .706L14.459 3.69l-2-2L13.502.646a.5.5 0 0 1 .707 0l1.293 1.293zm-1.75 2.456-2-2L4.939 9.21a.5.5 0 0 0-.121.196l-.805 2.414a.25.25 0 0 0 .316.316l2.414-.805a.5.5 0 0 0 .196-.12l6.813-6.814z"/>
251+
+ <path fill-rule="evenodd" d="M1 13.5A1.5 1.5 0 0 0 2.5 15h11a1.5 1.5 0 0 0 1.5-1.5v-6a.5.5 0 0 0-1 0v6a.5.5 0 0 1-.5.5h-11a.5.5 0 0 1-.5-.5v-11a.5.5 0 0 1 .5-.5H9a.5.5 0 0 0 0-1H2.5A1.5 1.5 0 0 0 1 2.5v11z"/>
252+
+ </svg>
253+
+ </button>
254+
+ <button onclick="actions.deleteMessage('${id}')" class="btn btn-danger">
255+
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-trash-fill" viewBox="0 0 16 16">
256+
+ <path d="M2.5 1a1 1 0 0 0-1 1v1a1 1 0 0 0 1 1H3v9a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V4h.5a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H10a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1H2.5zm3 4a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 .5-.5zM8 5a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7A.5.5 0 0 1 8 5zm3 .5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 1 0z"/>
257+
+ </svg>
258+
+ </button>
259+
+ </div>
260+
+ </div>
261+
+ <br/>
262+
+ </div>
263+
+ `
264+
+ }
265+
};
266+
```
267+
268+
___
269+
270+
* Add onload code - `index.html`
271+
```diff
272+
<!doctype html>
273+
<html lang="en">
274+
<head>
275+
<meta charset="utf-8">
276+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
277+
<meta name="viewport" content="width=device-width, initial-scale=1">
278+
<title>Messenger App</title>
279+
<style>
280+
#footer {
281+
position: fixed;
282+
left: 0;
283+
bottom: 0;
284+
width: 100%;
285+
}
286+
</style>
287+
<!-- Bootstrap CSS Stylesheet-->
288+
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"
289+
integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
290+
<!-- Bootstrap Javascript-->
291+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"
292+
integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p"
293+
crossorigin="anonymous"></script>
294+
<!-- Axios HTTP Library -->
295+
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
296+
</head>
297+
298+
<body>
299+
<div class="container">
300+
<!-- Title header -->
301+
<h1>Messenger App</h1>
302+
<hr />
303+
<!-- Bootstrap Accordion https://getbootstrap.com/docs/5.0/components/accordion START -->
304+
<div class="accordion" id="accordionExample" style="margin-bottom: 25px">
305+
<div class="accordion-item">
306+
<h2 class="accordion-header" id="headingOne">
307+
<button class="accordion-button" type="button" data-bs-toggle="collapse"
308+
data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
309+
Authors
310+
</button>
311+
</h2>
312+
<div id="collapseOne" class="accordion-collapse collapse show" aria-labelledby="headingOne"
313+
data-bs-parent="#accordionExample">
314+
<div id="authors" class="accordion-body">
315+
</div>
316+
</div>
317+
</div>
318+
</div>
319+
<!-- Bootstrap Accordion END -->
320+
<!-- Card container (Houses all cards/messages) START -->
321+
<div class="row" id="cards" style="margin-bottom: 50px">
322+
</div>
323+
<!-- Footer (sticks to the bottom of the screen) -->
324+
<div id="footer">
325+
<button style="width: 100%" class="btn btn-primary">Create</button>
326+
</div>
327+
328+
</div>
329+
<!-- External custom scripts -->
330+
<script src="API.js"></script>
331+
<script src="utils.js"></script>
332+
<script src="actions.js"></script>
333+
<script>
334+
- // More code will be here
335+
+ actions.refreshMessages()
336+
</script>
337+
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
338+
<script src="https://code.jquery.com/jquery-1.12.4.min.js"
339+
integrity="sha384-nvAa0+6Qg9clwYCGGPpDQLVpLNn0fRaROjHqs13t4Ggj3Ez50XnGQqc/r8MhnRDZ"
340+
crossorigin="anonymous"></script>
341+
</body>
342+
</html>
343+
```

0 commit comments

Comments
 (0)