`
18 | }
19 |
20 | export { FurnitureCard }
--------------------------------------------------------------------------------
/07 Routing/Furniture/Components/NavLink.js:
--------------------------------------------------------------------------------
1 | import { html } from 'https://unpkg.com/lit-html?module'
2 |
3 | const NavLink = ({ attributes, text }, click) =>
4 | click
5 | ? html`
`
7 |
8 | export { NavLink }
--------------------------------------------------------------------------------
/07 Routing/Furniture/Components/PageLayout.js:
--------------------------------------------------------------------------------
1 | import { html } from 'https://unpkg.com/lit-html?module'
2 | import { Header } from './Header.js'
3 |
4 | const PageLayout = (...children) => html`
5 | ${Header()}
6 | ${children}`
7 |
8 | export { PageLayout }
--------------------------------------------------------------------------------
/07 Routing/Furniture/Readme.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | - Node JS
4 |
5 | __This project loads Lit-html and Page JS from CDN so it does not need anything else__
6 |
7 |
8 |
9 | 1. __run node server.js while at the server (server) folder in the terminal__
10 | 2. __run npx lite-server while in the root (Furniture) folder in the terminal__
11 |
12 |
13 |
14 | _If you don't want to or can't use IDE, you can do the same by navigating to the same
15 | folders in the normal Windows explorer, opening CMD in that directory and writing the same commands._
--------------------------------------------------------------------------------
/07 Routing/Furniture/Views/Dashboard.js:
--------------------------------------------------------------------------------
1 | import { html } from 'https://unpkg.com/lit-html?module'
2 | import { furniture } from '../requests/requests.js'
3 | import { FurnitureCard } from '../Components/FurnitureCard.js'
4 |
5 | const Dashboard = (items) => {
6 | document.title = 'Dashboard'
7 |
8 | return html`
9 |
10 |
11 |
12 |
Welcome to Furniture System
13 |
Select furniture from the catalog to view details.
14 |
15 |
16 |
17 | ${items.map(x => FurnitureCard(x))}
18 |
`
19 | }
20 |
21 | export { Dashboard }
--------------------------------------------------------------------------------
/07 Routing/Furniture/images/chair.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silverster0113/JS-Applications-February-2021/878573b5a22907f709083ce25dfcd511dcb78cce/07 Routing/Furniture/images/chair.jpg
--------------------------------------------------------------------------------
/07 Routing/Furniture/images/sofa.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silverster0113/JS-Applications-February-2021/878573b5a22907f709083ce25dfcd511dcb78cce/07 Routing/Furniture/images/sofa.jpg
--------------------------------------------------------------------------------
/07 Routing/Furniture/images/table.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silverster0113/JS-Applications-February-2021/878573b5a22907f709083ce25dfcd511dcb78cce/07 Routing/Furniture/images/table.png
--------------------------------------------------------------------------------
/07 Routing/Furniture/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
8 |
Furniture
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/07 Routing/Furniture/requests/backendAPI.js:
--------------------------------------------------------------------------------
1 | const origin = 'http://localhost:3030'
2 |
3 | const createOptions = (method = 'GET', body) => {
4 | const options = {
5 | method,
6 | headers: {},
7 | }
8 | const token = sessionStorage.getItem('accessToken')
9 |
10 | if (token) {
11 | options.headers['X-Authorization'] = token
12 | }
13 | if (body) {
14 | options.headers['Content-Type'] = 'application/json'
15 | options.body = JSON.stringify(body)
16 | }
17 |
18 | return options
19 | }
20 |
21 | const request = async (uri, options) => {
22 | const URI = uri[0] === '/' ? uri : `/${uri}`
23 | let response = {}
24 |
25 | try {
26 | response = await fetch(`${origin}${URI}`, options)
27 | } catch (e) {
28 | throw new Error(e)
29 | }
30 |
31 | if (! response.ok) {
32 | throw new Error(response.status)
33 | }
34 |
35 | try {
36 | return await response.json()
37 | } catch (e) {
38 | return response
39 | }
40 | }
41 |
42 | const fetches = {
43 | get: async (uri) => await request(uri, createOptions()),
44 | post: async (uri, body) => await request(uri, createOptions('POST', body)),
45 | put: async (uri, body) => await request(uri, createOptions('PUT', body)),
46 | delete: async (uri) => await request(uri, createOptions('DELETE')),
47 | }
48 |
49 | export { fetches }
--------------------------------------------------------------------------------
/07 Routing/Furniture/requests/requests.js:
--------------------------------------------------------------------------------
1 | import { fetches } from './backendAPI.js'
2 |
3 | const user = {
4 | register: (body) => fetches.post('users/register', body),
5 | login: (body) => fetches.post('users/login', body),
6 | logout: () => fetches.get('users/logout'),
7 | }
8 |
9 | const furniture = {
10 | create: (body) => fetches.post('data/catalog', body),
11 | getAll: () => fetches.get('data/catalog'),
12 | details: (id) => fetches.get(`data/catalog/${id}`),
13 | update: (body, id) => fetches.put(`data/catalog/${id}`, body),
14 | delete: (id) => fetches.delete(`data/catalog/${id}`),
15 | my: (id) => fetches.get(`data/catalog?where=_ownerId%3D%22${id}%22`),
16 | }
17 |
18 | export { user, furniture }
--------------------------------------------------------------------------------
/07 Routing/Furniture/server/data/messenger.json:
--------------------------------------------------------------------------------
1 | {
2 | "-LxHVtajG3N1sU714pVj": {
3 | "author": "Spami",
4 | "content": "Hello, are you there?"
5 | },
6 | "-LxIDxC-GotWtf4eHwV8": {
7 | "author": "Garry",
8 | "content": "Yep, whats up :?"
9 | },
10 | "-LxIDxPfhsNipDrOQ5g_": {
11 | "author": "Spami",
12 | "content": "How are you? Long time no see? :)"
13 | },
14 | "-LxIE-dM_msaz1O9MouM": {
15 | "author": "George",
16 | "content": "Hello, guys! :))"
17 | },
18 | "-LxLgX_nOIiuvbwmxt8w": {
19 | "author": "Spami",
20 | "content": "Hello, George nice to see you! :)))"
21 | }
22 | }
--------------------------------------------------------------------------------
/07 Routing/Furniture/server/data/phonebook.json:
--------------------------------------------------------------------------------
1 | {
2 | "2d5ae478-87c7-45fa-acf9-f04aa4724421": {
3 | "person": "Maya",
4 | "phone": "+1-555-7653",
5 | "_id": "2d5ae478-87c7-45fa-acf9-f04aa4724421"
6 | },
7 | "6012c542-38e1-4660-ba40-1b109c40cb2f": {
8 | "person": "John",
9 | "phone": "+1-555-4986",
10 | "_id": "6012c542-38e1-4660-ba40-1b109c40cb2f"
11 | },
12 | "d749a819-1e41-4c65-9ce2-7b429c4ebd0d": {
13 | "person": "Nicolle",
14 | "phone": "+1-555-9124",
15 | "_id": "d749a819-1e41-4c65-9ce2-7b429c4ebd0d"
16 | }
17 | }
--------------------------------------------------------------------------------
/07 Routing/Furniture/src/contextAPI.js:
--------------------------------------------------------------------------------
1 | import { furniture } from '../requests/requests.js'
2 |
3 | const context = {
4 | storeAllFurniture: async (context, next) => {
5 | context.allFurniture = await furniture.getAll()
6 | next()
7 | },
8 | storeFurnitureItem: async (context, next) => {
9 | context.currentItem = await furniture.details(context.params.id)
10 | context.currentItem.isOwn = context.currentItem._ownerId === sessionStorage.getItem('id')
11 | next()
12 | },
13 | storeMyFurniture: async (context, next) => {
14 | const myId = sessionStorage.getItem('id')
15 | context.myFurniture = await furniture.my(myId)
16 | next()
17 | }
18 | }
19 |
20 | export { context }
--------------------------------------------------------------------------------
/07 Routing/Furniture/src/helper.js:
--------------------------------------------------------------------------------
1 | const isUserLogged = () => sessionStorage.getItem('accessToken') ? true : false
2 |
3 | const createFormObject = (form) => {
4 | const formData = new FormData(form)
5 |
6 | return Object.fromEntries([...formData.entries()])
7 | }
8 |
9 | export { isUserLogged, createFormObject }
--------------------------------------------------------------------------------
/08 Modular Applications/TeamManager/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/08 Modular Applications/TeamManager/Components/Footer.js:
--------------------------------------------------------------------------------
1 | import { html } from '../node_modules/lit-html/lit-html.js'
2 |
3 | const Footer = () => html`
4 | `
7 |
8 | export { Footer }
--------------------------------------------------------------------------------
/08 Modular Applications/TeamManager/Components/Header.js:
--------------------------------------------------------------------------------
1 | import { html } from '../node_modules/lit-html/lit-html.js'
2 | import { user } from '../requests/requests.js'
3 | import { isUserLogged } from '../src/helper.js'
4 | import page from '../node_modules/page/page.mjs'
5 |
6 | const logout = async () => {
7 | try {
8 | await user.logout()
9 | } catch (e) {
10 | console.log(e)
11 | }
12 |
13 | sessionStorage.clear()
14 | page.redirect('/')
15 | }
16 |
17 | const NavLink = ({ route, text }) =>
18 | route === 'javascript:void(0)'
19 | ? html`
${text} `
20 | : html`
${text} `
21 |
22 |
23 | const getHeaderLinks = (loggedIn) => {
24 | const linkObj = (route, text) => ({ route, text })
25 |
26 | return loggedIn
27 | ? [
28 | linkObj('/browse', 'Browse Teams'),
29 | linkObj('/myTeams', 'My Teams'),
30 | linkObj('javascript:void(0)', 'Logout'),
31 | ] : [
32 | linkObj('/browse', 'Browse Teams'),
33 | linkObj('/login', 'Login'),
34 | linkObj('/register', 'Register'),
35 | ]
36 | }
37 |
38 |
39 | const Header = () => html`
40 |
41 | Team Manager
42 |
43 | ${getHeaderLinks(isUserLogged()).map(x => NavLink(x))}
44 |
45 | `
46 |
47 | export { Header }
--------------------------------------------------------------------------------
/08 Modular Applications/TeamManager/Components/PageLayout.js:
--------------------------------------------------------------------------------
1 | import { html } from '../node_modules/lit-html/lit-html.js'
2 | import { Header } from './Header.js'
3 | import { Footer } from './Footer.js'
4 |
5 | const PageLayout = (...children) => html`
6 | ${Header()}
7 |
8 | ${children}
9 |
10 | ${Footer()}`
11 |
12 | export { PageLayout }
--------------------------------------------------------------------------------
/08 Modular Applications/TeamManager/README.md:
--------------------------------------------------------------------------------
1 |
Prerequisites:
2 |
3 | -
Node JS
4 | -
lit-html
5 | -
Page JS
6 |
7 |
Execution:
8 |
9 | 1. __run npm install while at the root(TeamManager) folder in terminal for lit-html and Page JS install__
10 | 2. __run node server.js while at the server (server) folder in the terminal to run the Node JS server__
11 | 3. __run npx lite-server while in the root (TeamManager) folder in the terminal, or use any server you like (http-server, live-server) etc.__
12 |
13 |
Alternatively:
14 |
15 | _If you don't want to or can't use IDE, you can do the same by navigating to the same
16 | folders in the normal Windows explorer, opening CMD in that directory and writing the same commands._
--------------------------------------------------------------------------------
/08 Modular Applications/TeamManager/Team manager task.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silverster0113/JS-Applications-February-2021/878573b5a22907f709083ce25dfcd511dcb78cce/08 Modular Applications/TeamManager/Team manager task.docx
--------------------------------------------------------------------------------
/08 Modular Applications/TeamManager/Views/BrowseView.js:
--------------------------------------------------------------------------------
1 | import { html, nothing } from '../node_modules/lit-html/lit-html.js'
2 |
3 | const TeamComponent = ({ _id, logoUrl, name, description, membersCount }) => html`
4 |
5 |
6 |
7 |
${name}
8 |
${description}
9 |
${membersCount} Member${membersCount === 1 ? nothing :'s'}
10 |
11 |
12 | `
13 |
14 | const BrowseView = (isUserLogged, teams) => html`
15 |
16 |
17 | Team Browser
18 |
19 |
20 | ${isUserLogged ? html`
21 |
22 |
23 | ` : nothing}
24 |
25 | ${teams.map(x => TeamComponent(x))}
26 | `
27 |
28 | export { BrowseView }
--------------------------------------------------------------------------------
/08 Modular Applications/TeamManager/Views/HomeView.js:
--------------------------------------------------------------------------------
1 | import { html } from '../node_modules/lit-html/lit-html.js'
2 | import { isUserLogged } from '../src/helper.js'
3 |
4 | const BtnComponent = (isUserLogged) => html`
5 |
${isUserLogged
6 | ? 'Browse Teams'
7 | : 'Sign Up Now'} `
8 |
9 | const HomeView = () => html`
10 |
11 |
12 |
13 |
14 |
Welcome to Team Manager!
15 |
Want to organize your peers? Create and manage a team for free.
16 |
Looking for a team to join? Browse our communities and find like-minded
17 | people!
18 | ${BtnComponent(isUserLogged())}
19 |
20 |
21 | `
22 |
23 | export { HomeView }
--------------------------------------------------------------------------------
/08 Modular Applications/TeamManager/assets/atat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silverster0113/JS-Applications-February-2021/878573b5a22907f709083ce25dfcd511dcb78cce/08 Modular Applications/TeamManager/assets/atat.png
--------------------------------------------------------------------------------
/08 Modular Applications/TeamManager/assets/hydrant.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silverster0113/JS-Applications-February-2021/878573b5a22907f709083ce25dfcd511dcb78cce/08 Modular Applications/TeamManager/assets/hydrant.png
--------------------------------------------------------------------------------
/08 Modular Applications/TeamManager/assets/rocket.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silverster0113/JS-Applications-February-2021/878573b5a22907f709083ce25dfcd511dcb78cce/08 Modular Applications/TeamManager/assets/rocket.png
--------------------------------------------------------------------------------
/08 Modular Applications/TeamManager/assets/team.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silverster0113/JS-Applications-February-2021/878573b5a22907f709083ce25dfcd511dcb78cce/08 Modular Applications/TeamManager/assets/team.png
--------------------------------------------------------------------------------
/08 Modular Applications/TeamManager/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
Team Manager
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/08 Modular Applications/TeamManager/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "TeamManager",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "isarray": {
8 | "version": "0.0.1",
9 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
10 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
11 | "dev": true
12 | },
13 | "lit-html": {
14 | "version": "1.3.0",
15 | "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-1.3.0.tgz",
16 | "integrity": "sha512-0Q1bwmaFH9O14vycPHw8C/IeHMk/uSDldVLIefu/kfbTBGIc44KGH6A8p1bDfxUfHdc8q6Ct7kQklWoHgr4t1Q==",
17 | "dev": true
18 | },
19 | "page": {
20 | "version": "1.11.6",
21 | "resolved": "https://registry.npmjs.org/page/-/page-1.11.6.tgz",
22 | "integrity": "sha512-P6e2JfzkBrPeFCIPplLP7vDDiU84RUUZMrWdsH4ZBGJ8OosnwFkcUkBHp1DTIjuipLliw9yQn/ZJsXZvarsO+g==",
23 | "dev": true,
24 | "requires": {
25 | "path-to-regexp": "~1.2.1"
26 | }
27 | },
28 | "path-to-regexp": {
29 | "version": "1.2.1",
30 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.2.1.tgz",
31 | "integrity": "sha1-szcFwUAjTYc8hyHHuf2LVB7Tr/k=",
32 | "dev": true,
33 | "requires": {
34 | "isarray": "0.0.1"
35 | }
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/08 Modular Applications/TeamManager/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "TeamManager",
3 | "version": "1.0.0",
4 | "description": "
Prerequisites: ",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "devDependencies": {
13 | "lit-html": "^1.3.0",
14 | "page": "^1.11.6"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/08 Modular Applications/TeamManager/requests/backendAPI.js:
--------------------------------------------------------------------------------
1 | const origin = 'http://localhost:3030'
2 |
3 | const createOptions = (method = 'GET', body) => {
4 | const options = {
5 | method,
6 | headers: {},
7 | }
8 | const token = sessionStorage.getItem('accessToken')
9 |
10 | if (token) {
11 | options.headers['X-Authorization'] = token
12 | }
13 | if (body) {
14 | options.headers['Content-Type'] = 'application/json'
15 | options.body = JSON.stringify(body)
16 | }
17 |
18 | return options
19 | }
20 |
21 | const request = async (uri, options) => {
22 | const URI = uri[0] === '/' ? uri : `/${uri}`
23 | let response = {}
24 |
25 | try {
26 | response = await fetch(`${origin}${URI}`, options)
27 | } catch (e) {
28 | throw new Error(e)
29 | }
30 |
31 | if (! response.ok) {
32 | throw new Error(response.status)
33 | }
34 |
35 | try {
36 | return await response.json()
37 | } catch (e) {
38 | return response
39 | }
40 | }
41 |
42 | const fetches = {
43 | get: async (uri) => await request(uri, createOptions()),
44 | post: async (uri, body) => await request(uri, createOptions('POST', body)),
45 | put: async (uri, body) => await request(uri, createOptions('PUT', body)),
46 | delete: async (uri) => await request(uri, createOptions('DELETE')),
47 | }
48 |
49 | export { fetches }
--------------------------------------------------------------------------------
/08 Modular Applications/TeamManager/requests/requests.js:
--------------------------------------------------------------------------------
1 | import { fetches } from './backendAPI.js'
2 |
3 | const user = {
4 | register: (body) => fetches.post('users/register', body),
5 | login: (body) => fetches.post('users/login', body),
6 | logout: () => fetches.get('users/logout'),
7 | }
8 |
9 | const teams = {
10 | getAll: () => fetches.get('/data/teams'),
11 | getAllMembers: () => fetches.get('/data/members?where=status%3D%22member%22'),
12 | createTeam: (body) => fetches.post('/data/teams', body),
13 | updateTeam: (id, body) => fetches.put(`/data/teams/${id}`, body),
14 | getTeam: (id) => fetches.get(`/data/teams/${id}`),
15 | becomeMember: body => fetches.post('/data/members', body),
16 | cancelRequest: id => fetches.delete(`/data/members/${id}`),
17 | approveRequest: (id, body) => fetches.put(`/data/members/${id}`, body),
18 | getAllTeamMembers: (teamId) => fetches.get(`/data/members?where=teamId%3D%22${teamId}%22&load=user%3D_ownerId%3Ausers`),
19 | getAllTeamsOfUser: (userId) => fetches.get(`/data/members?where=_ownerId%3D%22${userId}%22%20AND%20status%3D%22member%22&load=team%3DteamId%3Ateams`)
20 | }
21 |
22 | export { user, teams }
--------------------------------------------------------------------------------
/08 Modular Applications/TeamManager/server/data/messenger.json:
--------------------------------------------------------------------------------
1 | {
2 | "-LxHVtajG3N1sU714pVj": {
3 | "author": "Spami",
4 | "content": "Hello, are you there?"
5 | },
6 | "-LxIDxC-GotWtf4eHwV8": {
7 | "author": "Garry",
8 | "content": "Yep, whats up :?"
9 | },
10 | "-LxIDxPfhsNipDrOQ5g_": {
11 | "author": "Spami",
12 | "content": "How are you? Long time no see? :)"
13 | },
14 | "-LxIE-dM_msaz1O9MouM": {
15 | "author": "George",
16 | "content": "Hello, guys! :))"
17 | },
18 | "-LxLgX_nOIiuvbwmxt8w": {
19 | "author": "Spami",
20 | "content": "Hello, George nice to see you! :)))"
21 | }
22 | }
--------------------------------------------------------------------------------
/08 Modular Applications/TeamManager/server/data/phonebook.json:
--------------------------------------------------------------------------------
1 | {
2 | "2d5ae478-87c7-45fa-acf9-f04aa4724421": {
3 | "person": "Maya",
4 | "phone": "+1-555-7653",
5 | "_id": "2d5ae478-87c7-45fa-acf9-f04aa4724421"
6 | },
7 | "6012c542-38e1-4660-ba40-1b109c40cb2f": {
8 | "person": "John",
9 | "phone": "+1-555-4986",
10 | "_id": "6012c542-38e1-4660-ba40-1b109c40cb2f"
11 | },
12 | "d749a819-1e41-4c65-9ce2-7b429c4ebd0d": {
13 | "person": "Nicolle",
14 | "phone": "+1-555-9124",
15 | "_id": "d749a819-1e41-4c65-9ce2-7b429c4ebd0d"
16 | }
17 | }
--------------------------------------------------------------------------------
/08 Modular Applications/TeamManager/src/helper.js:
--------------------------------------------------------------------------------
1 | const isUserLogged = () => sessionStorage.getItem('accessToken') ? true : false
2 |
3 | const createFormObject = (form) => {
4 | const formData = new FormData(form)
5 |
6 | return Object.fromEntries([...formData.entries()])
7 | }
8 |
9 | const saveUserInStorage = data => {
10 | sessionStorage.setItem('email', data.email)
11 | sessionStorage.setItem('username', data.username)
12 | sessionStorage.setItem('id', data._id)
13 | sessionStorage.setItem('accessToken', data.accessToken)
14 |
15 | return data
16 | }
17 |
18 | export { isUserLogged, createFormObject, saveUserInStorage }
--------------------------------------------------------------------------------
/08 Modular Applications/TeamManager/static/modal.css:
--------------------------------------------------------------------------------
1 | .overlay {
2 | /* display: none; */
3 | background-color: rgba(100, 100, 100, 0.5);
4 | backdrop-filter: blur(5px);
5 | position: fixed;
6 | left: 0;
7 | top: 0;
8 | width: 100%;
9 | height: 100%;
10 | z-index: 100;
11 | }
12 |
13 | .modal {
14 | background-color: white;
15 | color: black;
16 | width: 500px;
17 | text-align: center;
18 | margin: auto;
19 | margin-top: 15vh;
20 | padding: 32px;
21 | }
22 |
23 | .modal p {
24 | margin-bottom: 32px;
25 | }
--------------------------------------------------------------------------------
/08 Modular Applications/TeamManager/static/site.css:
--------------------------------------------------------------------------------
1 | .hero {
2 | font-size: 125%;
3 | margin-top: 128px;
4 | }
5 |
6 | h2 {
7 | padding-bottom: 32px;
8 | }
9 |
10 | .tm-hero-col {
11 | margin-left: 314px;
12 | }
13 |
14 | .main-form label {
15 | display: block;
16 | margin: 16px auto;
17 | position: relative;
18 | }
19 |
20 | .main-form input[type="text"], .main-form input[type="password"] {
21 | padding: 4px 8px;
22 | display: block;
23 | width: 200px;
24 | position: absolute;
25 | top: 0;
26 | bottom: 0;
27 | right: 0;
28 | border: 1px solid #4f6457;
29 | }
30 |
31 | .main-form textarea {
32 | display: block;
33 | border: 1px solid #4f6457;
34 | width: 100%;
35 | resize: vertical;
36 | height: 300px;
37 | padding: 4px 8px;
38 | }
39 |
40 | .team-logo {
41 | width: 150px;
42 | height: 150px;
43 | margin: 32px;
44 | vertical-align: bottom;
45 | object-fit: contain;
46 | }
47 |
48 | .tm-preview {
49 | margin-left: 214px;
50 | padding: 32px;
51 | padding-left: 0;
52 | }
53 |
54 | .details {
55 | color: #999999;
56 | }
57 |
58 | .tm-members li {
59 | list-style: none;
60 | margin: 8px 0;
61 | }
62 |
63 | .clear {
64 | clear: both;
65 | }
66 |
67 | .action.tm-control {
68 | padding: 4px 8px;
69 | margin: 0 4px;
70 | }
71 |
72 | .error {
73 | text-align: center;
74 | color: red
75 | }
--------------------------------------------------------------------------------
/08 Modular Applications/TeamManager/styles.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silverster0113/JS-Applications-February-2021/878573b5a22907f709083ce25dfcd511dcb78cce/08 Modular Applications/TeamManager/styles.css
--------------------------------------------------------------------------------
/09 Exam Prep/MemeLounge/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/09 Exam Prep/MemeLounge/Components/Footer.js:
--------------------------------------------------------------------------------
1 | import { html } from '../node_modules/lit-html/lit-html.js'
2 |
3 | const Footer = () => html`
4 | `
7 |
8 | export { Footer }
--------------------------------------------------------------------------------
/09 Exam Prep/MemeLounge/Components/Notif.js:
--------------------------------------------------------------------------------
1 | import { html } from '../node_modules/lit-html/lit-html.js'
2 |
3 | const Notif = (msg) => {
4 | return html`
5 |
`
10 | }
11 |
12 | export { Notif }
--------------------------------------------------------------------------------
/09 Exam Prep/MemeLounge/Components/PageLayout.js:
--------------------------------------------------------------------------------
1 | import { html } from '../node_modules/lit-html/lit-html.js'
2 | import { Header } from './Header.js'
3 | import { Footer } from './Footer.js'
4 |
5 | const PageLayout = (...children) => html`
6 | ${Header()}
7 |
8 | ${children}
9 |
10 | ${Footer()}`
11 |
12 | export { PageLayout }
--------------------------------------------------------------------------------
/09 Exam Prep/MemeLounge/Meme-Longue_Условие.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silverster0113/JS-Applications-February-2021/878573b5a22907f709083ce25dfcd511dcb78cce/09 Exam Prep/MemeLounge/Meme-Longue_Условие.docx
--------------------------------------------------------------------------------
/09 Exam Prep/MemeLounge/Views/AllMemesView.js:
--------------------------------------------------------------------------------
1 | import { html } from '../node_modules/lit-html/lit-html.js'
2 |
3 | const SingleMemeComponent = ({ _id, title, imageUrl }) => {
4 | return html`
5 |
6 |
7 |
8 |
${title}
9 |
10 |
11 |
14 |
15 |
`
16 | }
17 |
18 | const AllMemesView = (memes) => {
19 | return html`
20 |
21 | All Memes
22 |
23 | ${memes && memes.length !== 0
24 | ? memes.map(SingleMemeComponent)
25 | : html`
No memes in database.
`}
26 |
27 | `
28 | }
29 |
30 | export { AllMemesView }
--------------------------------------------------------------------------------
/09 Exam Prep/MemeLounge/Views/DetailsView.js:
--------------------------------------------------------------------------------
1 | import { html, nothing } from '../node_modules/lit-html/lit-html.js'
2 |
3 | const DetailsView = ({ title, imageUrl, description, _id, _ownerId }) => {
4 | return html`
5 |
6 | Meme Title: ${title}
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
Meme Description
15 |
16 | ${description}
17 |
18 | ${_ownerId === sessionStorage.getItem('id')
19 | ? html`
Edit
20 |
Delete `
21 | : nothing}
22 |
23 |
24 | `
25 | }
26 |
27 | export { DetailsView }
--------------------------------------------------------------------------------
/09 Exam Prep/MemeLounge/Views/GuestHomeView.js:
--------------------------------------------------------------------------------
1 | import { html } from '../node_modules/lit-html/lit-html.js'
2 |
3 | const GuestHomeView = () => {
4 | return html`
5 |
6 |
7 |
Welcome To Meme Lounge
8 |
9 |
Login to see our memes right away!
10 |
14 |
15 | `
16 | }
17 |
18 | export { GuestHomeView }
--------------------------------------------------------------------------------
/09 Exam Prep/MemeLounge/Views/UserProfileView.js:
--------------------------------------------------------------------------------
1 | import { html } from '../node_modules/lit-html/lit-html.js'
2 |
3 | const SingleMemeComponent = ({ title, imageUrl, _id }) => html`
4 |
`
9 |
10 | const UserProfileView = ({ username, email, memes, gender }) => {
11 | return html`
12 |
13 |
14 |
16 |
17 |
Username: ${username}
18 |
Email: ${email}
19 |
My memes count: ${memes.length}
20 |
21 |
22 | User Memes
23 |
24 | ${memes.length !== 0
25 | ? memes.map(SingleMemeComponent)
26 | : html`
No memes in database.
`}
27 |
28 | `
29 | }
30 |
31 | export { UserProfileView }
--------------------------------------------------------------------------------
/09 Exam Prep/MemeLounge/images/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silverster0113/JS-Applications-February-2021/878573b5a22907f709083ce25dfcd511dcb78cce/09 Exam Prep/MemeLounge/images/1.png
--------------------------------------------------------------------------------
/09 Exam Prep/MemeLounge/images/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silverster0113/JS-Applications-February-2021/878573b5a22907f709083ce25dfcd511dcb78cce/09 Exam Prep/MemeLounge/images/2.png
--------------------------------------------------------------------------------
/09 Exam Prep/MemeLounge/images/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silverster0113/JS-Applications-February-2021/878573b5a22907f709083ce25dfcd511dcb78cce/09 Exam Prep/MemeLounge/images/3.png
--------------------------------------------------------------------------------
/09 Exam Prep/MemeLounge/images/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silverster0113/JS-Applications-February-2021/878573b5a22907f709083ce25dfcd511dcb78cce/09 Exam Prep/MemeLounge/images/4.png
--------------------------------------------------------------------------------
/09 Exam Prep/MemeLounge/images/6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silverster0113/JS-Applications-February-2021/878573b5a22907f709083ce25dfcd511dcb78cce/09 Exam Prep/MemeLounge/images/6.png
--------------------------------------------------------------------------------
/09 Exam Prep/MemeLounge/images/female.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silverster0113/JS-Applications-February-2021/878573b5a22907f709083ce25dfcd511dcb78cce/09 Exam Prep/MemeLounge/images/female.png
--------------------------------------------------------------------------------
/09 Exam Prep/MemeLounge/images/male.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silverster0113/JS-Applications-February-2021/878573b5a22907f709083ce25dfcd511dcb78cce/09 Exam Prep/MemeLounge/images/male.png
--------------------------------------------------------------------------------
/09 Exam Prep/MemeLounge/images/welcome-meme.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silverster0113/JS-Applications-February-2021/878573b5a22907f709083ce25dfcd511dcb78cce/09 Exam Prep/MemeLounge/images/welcome-meme.jpg
--------------------------------------------------------------------------------
/09 Exam Prep/MemeLounge/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
Meme Lounge
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/09 Exam Prep/MemeLounge/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "meme-lounge",
3 | "version": "1.0.0",
4 | "description": "Meme Lounge Single Page Application",
5 | "main": "index.html",
6 | "scripts": {
7 | "test": "mocha tests",
8 | "start": "http-server -a localhost -p 3000 -P http://localhost:3000?"
9 | },
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "lit-html": "^1.3.0",
14 | "page": "^1.11.6"
15 | },
16 | "devDependencies": {
17 | "chai": "^4.3.4",
18 | "http-server": "^0.12.3",
19 | "mocha": "^8.3.2",
20 | "playwright-chromium": "^1.9.2"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/09 Exam Prep/MemeLounge/requests/backendAPI.js:
--------------------------------------------------------------------------------
1 | const origin = 'http://localhost:3030'
2 |
3 | const createOptions = (method = 'GET', body) => {
4 | const options = {
5 | method,
6 | headers: {},
7 | }
8 | const token = sessionStorage.getItem('accessToken')
9 |
10 | if (token) {
11 | options.headers['X-Authorization'] = token
12 | }
13 | if (body) {
14 | options.headers['Content-Type'] = 'application/json'
15 | options.body = JSON.stringify(body)
16 | }
17 |
18 | return options
19 | }
20 |
21 | const request = async (uri, options) => {
22 | const URI = uri[0] === '/' ? uri : `/${uri}`
23 | let response = {}
24 |
25 | try {
26 | response = await fetch(`${origin}${URI}`, options)
27 | } catch (e) {
28 | throw new Error(e)
29 | }
30 |
31 | if (! response.ok) {
32 | throw new Error(response.status)
33 | }
34 |
35 | try {
36 | return await response.json()
37 | } catch (e) {
38 | return response
39 | }
40 | }
41 |
42 | const fetches = {
43 | get: async (uri) => await request(uri, createOptions()),
44 | post: async (uri, body) => await request(uri, createOptions('POST', body)),
45 | put: async (uri, body) => await request(uri, createOptions('PUT', body)),
46 | delete: async (uri) => await request(uri, createOptions('DELETE')),
47 | }
48 |
49 | export { fetches }
--------------------------------------------------------------------------------
/09 Exam Prep/MemeLounge/requests/requests.js:
--------------------------------------------------------------------------------
1 | import { fetches } from './backendAPI.js'
2 |
3 | const user = {
4 | register: (body) => fetches.post('users/register', body),
5 | login: (body) => fetches.post('users/login', body),
6 | logout: () => fetches.get('users/logout'),
7 | }
8 |
9 | const memes = {
10 | getAllMemes: () => fetches.get(`/data/memes?sortBy=_createdOn%20desc`),
11 | createMeme: ({ title, description, imageUrl }) => fetches.post(
12 | `/data/memes`,
13 | { title, description, imageUrl }
14 | ),
15 | getMeme: (id) => fetches.get(`/data/memes/${id}`),
16 | deleteMeme: (memeId) => fetches.delete(`/data/memes/${memeId}`),
17 | updateMeme: (memeId, body) => fetches.put(`/data/memes/${memeId}`, body),
18 | getAllUserMemes: (userId) => fetches.get(`/data/memes?where=_ownerId%3D%22${userId}%22&sortBy=_createdOn%20desc`)
19 | }
20 |
21 | export { user, memes }
--------------------------------------------------------------------------------
/09 Exam Prep/MemeLounge/src/contextAPI.js:
--------------------------------------------------------------------------------
1 | import { GuestHomeView } from '../Views/GuestHomeView.js'
2 | import { AllMemesView } from '../Views/AllMemesView.js'
3 | import { memes } from '../requests/requests.js'
4 |
5 | const contextAPI = {
6 | pickHomePage: async (context, next) => {
7 | const allMemes = await memes.getAllMemes()
8 |
9 | context.homePage = sessionStorage.getItem('accessToken')
10 | ? () => AllMemesView(allMemes)
11 | : GuestHomeView
12 |
13 | next()
14 | },
15 | storeAllMemes: async (context, next) => {
16 | context.allMemes = await memes.getAllMemes()
17 | next()
18 | },
19 | storeMeme: async (context, next) => {
20 | context.meme = await memes.getMeme(context.params.id)
21 | next()
22 | },
23 | storeUserData: async (context, next) => {
24 | context.user = {
25 | email: sessionStorage.getItem('email'),
26 | username: sessionStorage.getItem('username'),
27 | gender: sessionStorage.getItem('gender'),
28 | memes: await memes.getAllUserMemes(sessionStorage.getItem('id'))
29 | }
30 | next()
31 | }
32 | }
33 |
34 | export { contextAPI }
--------------------------------------------------------------------------------
/09 Exam Prep/MemeLounge/src/helper.js:
--------------------------------------------------------------------------------
1 | import { render } from '../node_modules/lit-html/lit-html.js'
2 | import { PageLayout } from '../Components/PageLayout.js'
3 |
4 | const container = document.getElementById('container')
5 | const renderView = (view) => render(PageLayout(view), container)
6 |
7 | const isUserLogged = () => sessionStorage.getItem('accessToken') ? true : false
8 |
9 | const createFormObject = (form) => {
10 | const formData = new FormData(form)
11 |
12 | return Object.fromEntries([...formData.entries()])
13 | }
14 |
15 | const saveUserInStorage = data => {
16 | sessionStorage.setItem('email', data.email)
17 | sessionStorage.setItem('username', data.username)
18 | sessionStorage.setItem('id', data._id)
19 | sessionStorage.setItem('accessToken', data.accessToken)
20 | sessionStorage.setItem('gender', data.gender)
21 |
22 | return data
23 | }
24 |
25 | const showNotification = (view, msg) => {
26 | renderView(view(msg))
27 |
28 | setTimeout(() => renderView(view(msg)), 3000)
29 | }
30 |
31 | export { isUserLogged, createFormObject, saveUserInStorage, renderView, showNotification }
--------------------------------------------------------------------------------
/09 Exam Prep/MemeLounge/style/footer.css:
--------------------------------------------------------------------------------
1 | .footer {
2 | position: fixed;
3 | left: 0;
4 | bottom: 0;
5 | width: 100%;
6 | background-color: #333333;
7 | color: white;
8 | text-align: center;
9 | }
--------------------------------------------------------------------------------
/09 Exam Prep/MemeLounge/style/my-memes.css:
--------------------------------------------------------------------------------
1 | .meme-listings {
2 | width: 86%;
3 | height: 100%;
4 | display: flex;
5 | flex-wrap: wrap;
6 | margin: auto;
7 | }
8 |
9 | .my-meme {
10 | width: 25%;
11 | }
12 |
13 | .my-meme>img {
14 | margin: auto;
15 | display: block;
16 | width: 100%;
17 | cursor: pointer;
18 | }
19 |
20 | .meme-title {
21 | font-size: 30px;
22 | text-decoration: none;
23 | color: black;
24 | }
25 |
26 | .meme-title:hover {
27 | color: #4438ea;
28 | }
29 |
30 | #container>div.my-memes>div>div>div {
31 | padding-top: 20px;
32 | }
33 |
34 | .my-meme-btn {
35 | margin-top: 15px;
36 | background-color: blue;
37 | color: white;
38 | padding: 16px 20px;
39 | margin: 8px 0;
40 | border: none;
41 | cursor: pointer;
42 | width: 50%;
43 | opacity: 0.9;
44 | }
--------------------------------------------------------------------------------
/09 Exam Prep/MemeLounge/style/navigation.css:
--------------------------------------------------------------------------------
1 | nav {
2 | background-color: #333;
3 | overflow: hidden;
4 | }
5 |
6 |
7 | /* Style the links inside the navigation bar */
8 |
9 | nav a, nav span {
10 | float: left;
11 | color: #f2f2f2;
12 | text-align: center;
13 | padding: 14px 16px;
14 | text-decoration: none;
15 | font-size: 17px;
16 | }
17 |
18 | /* Change the color of links on hover */
19 |
20 | nav a:hover {
21 | background-color: #ddd;
22 | color: black;
23 | }
24 |
25 | /* Add a color to the active/current link */
26 | nav a, #common {
27 | float: left;
28 | }
29 | nav a.active {
30 | background-color: #0069d9;
31 | color: white;
32 | }
33 |
34 | .profile {
35 | float: right;
36 | }
37 |
38 | /* nav div.user {
39 | display: none;
40 | } */
--------------------------------------------------------------------------------
/09 Exam Prep/MemeLounge/style/notifications.css:
--------------------------------------------------------------------------------
1 | #notifications {
2 | float: right;
3 | position: absolute;
4 | top: 0;
5 | right: 0;
6 | text-align: center;
7 | margin: 1em;
8 | }
9 |
10 | .notification {
11 | font-size: 1.5em;
12 | color: #ffffff;
13 | display: block;
14 | text-align: right;
15 | }
16 |
17 | .notification>span {
18 | display: inline-block;
19 | padding: 0.5em 2em 0.5em 2em;
20 | border-radius: 10px;
21 | }
22 |
23 | #loadingBox>span {
24 | background: #7CB3E9;
25 | }
26 | #infoBox>span {
27 | background: #393;
28 | }
29 | #errorBox>span {
30 | background: #F50;
31 | }
--------------------------------------------------------------------------------
/09 Exam Prep/MemeLounge/style/site.css:
--------------------------------------------------------------------------------
1 | * {
2 | padding: 0;
3 | margin: 0;
4 | }
5 |
6 | body {
7 | font-family: 'Lato', sans-serif;
8 | overflow-x: hidden;
9 | }
10 |
11 | img {
12 | box-shadow: 2px 5px 13px 0px #000000, 8px -15px 31px -12px rgb(0 0 0 / 0%);
13 | margin-bottom: 15px;
14 | }
15 |
16 | .container {
17 | width: 50%;
18 | margin: auto;
19 | }
20 |
21 | .container.signin {
22 | margin-top: 20px;
23 | }
24 |
25 | textarea {
26 | height: 80px;
27 | }
--------------------------------------------------------------------------------
/09 Exam Prep/MemeLounge/style/styles.css:
--------------------------------------------------------------------------------
1 | @import './navigation.css';
2 | @import './site.css';
3 | @import './welcome.css';
4 | @import './notifications.css';
5 | @import './login-register.css';
6 | @import './meme-feed.css';
7 | @import './my-memes.css';
8 | @import './meme-details.css';
9 | @import './user-profile.css';
10 | @import './footer.css';
11 |
--------------------------------------------------------------------------------
/10 Exam Prep 2/CarTube/CarTube_Условие.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silverster0113/JS-Applications-February-2021/878573b5a22907f709083ce25dfcd511dcb78cce/10 Exam Prep 2/CarTube/CarTube_Условие.docx
--------------------------------------------------------------------------------
/10 Exam Prep 2/CarTube/Components/Footer.js:
--------------------------------------------------------------------------------
1 | import { html } from '../node_modules/lit-html/lit-html.js'
2 |
3 | const Footer = () => html`
4 |
5 | © All rights reserved
6 | `
7 |
8 | export { Footer }
--------------------------------------------------------------------------------
/10 Exam Prep 2/CarTube/Components/Header.js:
--------------------------------------------------------------------------------
1 | import { html } from '../node_modules/lit-html/lit-html.js'
2 | import { user } from '../requests/requests.js'
3 | import { isUserLogged } from '../src/helper.js'
4 | import page from '../node_modules/page/page.mjs'
5 |
6 | const logout = async () => {
7 | try {
8 | await user.logout()
9 | } catch (e) {
10 | console.log(e)
11 | }
12 |
13 | sessionStorage.clear()
14 | page.redirect('/')
15 | }
16 |
17 | const getHeaderLinks = (loggedIn) => {
18 | const username = sessionStorage.getItem('username')
19 |
20 | return loggedIn
21 | ? html`
22 |
`
28 | : html`
29 |
`
33 | }
34 |
35 | const Header = () => html`
36 |
37 | Home
38 | All Listings
39 | By Year
40 | ${getHeaderLinks(isUserLogged())}
41 | `
42 |
43 | export { Header }
--------------------------------------------------------------------------------
/10 Exam Prep 2/CarTube/Components/Listing.js:
--------------------------------------------------------------------------------
1 | import { html } from '../node_modules/lit-html/lit-html.js'
2 |
3 | const ListingComponent = ({ _id, brand, model, year, price, imageUrl }) => {
4 |
5 | return html`
6 |
7 |
8 |
9 |
10 |
${brand} ${model}
11 |
12 |
13 |
Year: ${year}
14 | Price: ${price} $
15 |
16 |
19 |
20 |
`
21 | }
22 |
23 | export { ListingComponent }
--------------------------------------------------------------------------------
/10 Exam Prep 2/CarTube/Components/Notif.js:
--------------------------------------------------------------------------------
1 | import { html } from '../node_modules/lit-html/lit-html.js'
2 |
3 | const Notif = (msg) => {
4 | return html`
5 |
`
10 | }
11 |
12 | export { Notif }
--------------------------------------------------------------------------------
/10 Exam Prep 2/CarTube/Components/PageLayout.js:
--------------------------------------------------------------------------------
1 | import { html } from '../node_modules/lit-html/lit-html.js'
2 | import { Header } from './Header.js'
3 | import { Footer } from './Footer.js'
4 |
5 | const PageLayout = (...children) => html`
6 | ${Header()}
7 |
8 | ${children}
9 |
10 | ${Footer()}`
11 |
12 | export { PageLayout }
--------------------------------------------------------------------------------
/10 Exam Prep 2/CarTube/Views/AllListingsView.js:
--------------------------------------------------------------------------------
1 | import { html } from '../node_modules/lit-html/lit-html.js'
2 | import { ListingComponent } from '../Components/Listing.js'
3 |
4 |
5 | const AllListingsView = (listings) => {
6 | return html`
7 |
8 | Car Listings
9 |
10 | ${listings.length > 0
11 | ? listings.map(ListingComponent)
12 | : html`
No cars in database.
`}
13 |
14 | `
15 | }
16 |
17 | export { AllListingsView }
--------------------------------------------------------------------------------
/10 Exam Prep 2/CarTube/Views/ByYearView.js:
--------------------------------------------------------------------------------
1 | import { html } from '../node_modules/lit-html/lit-html.js'
2 | import { ListingComponent } from '../Components/Listing.js'
3 | import page from '../node_modules/page/page.mjs'
4 |
5 | const displayMatches = async () => {
6 | const year = document.getElementById('search-input').value
7 |
8 | page.redirect(`/byYear?year=${year}`)
9 | }
10 |
11 | const ByYearView = (matches, year) => {
12 | console.log(matches, year)
13 |
14 | return html`
15 |
16 | Filter by year
17 |
18 |
19 |
21 | Search
22 |
23 |
24 |
25 | Results:
26 |
27 | ${matches.length > 0
28 | ? matches.map(ListingComponent)
29 | : html`
No results.
`}
30 |
31 | `
32 | }
33 |
34 | export { ByYearView }
--------------------------------------------------------------------------------
/10 Exam Prep 2/CarTube/Views/HomePageView.js:
--------------------------------------------------------------------------------
1 | import { html } from '../node_modules/lit-html/lit-html.js'
2 |
3 | const HomePageView = () => {
4 |
5 | return html`
6 |
7 |
8 |
Welcome To Car Tube
9 |
10 |
To see all the listings click the link below:
11 |
14 |
15 | `
16 | }
17 |
18 | export { HomePageView }
--------------------------------------------------------------------------------
/10 Exam Prep 2/CarTube/Views/MyListingsView.js:
--------------------------------------------------------------------------------
1 | import { html } from '../node_modules/lit-html/lit-html.js'
2 |
3 | const ListingComponent = ({ _id, imageUrl, brand, model, price, year }) => {
4 |
5 | return html`
6 |
7 |
8 |
9 |
10 |
${brand} ${model}
11 |
12 |
13 |
Year: ${year}
14 | Price: ${price} $
15 |
16 |
19 |
20 |
`
21 | }
22 |
23 | const MyListingsView = (listings) => {
24 |
25 | return html`
26 |
27 | My car listings
28 |
29 | ${listings.length > 0
30 | ? listings.map(ListingComponent)
31 | : html`
You haven't listed any cars yet.
`}
32 |
33 | `
34 | }
35 |
36 | export { MyListingsView }
--------------------------------------------------------------------------------
/10 Exam Prep 2/CarTube/images/audia3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silverster0113/JS-Applications-February-2021/878573b5a22907f709083ce25dfcd511dcb78cce/10 Exam Prep 2/CarTube/images/audia3.jpg
--------------------------------------------------------------------------------
/10 Exam Prep 2/CarTube/images/benz.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silverster0113/JS-Applications-February-2021/878573b5a22907f709083ce25dfcd511dcb78cce/10 Exam Prep 2/CarTube/images/benz.jpg
--------------------------------------------------------------------------------
/10 Exam Prep 2/CarTube/images/bmw.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silverster0113/JS-Applications-February-2021/878573b5a22907f709083ce25dfcd511dcb78cce/10 Exam Prep 2/CarTube/images/bmw.jpg
--------------------------------------------------------------------------------
/10 Exam Prep 2/CarTube/images/car-png.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silverster0113/JS-Applications-February-2021/878573b5a22907f709083ce25dfcd511dcb78cce/10 Exam Prep 2/CarTube/images/car-png.webp
--------------------------------------------------------------------------------
/10 Exam Prep 2/CarTube/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
Car Tube
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/10 Exam Prep 2/CarTube/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "car-tube",
3 | "version": "1.0.0",
4 | "description": "Car Tube Single Page Application",
5 | "main": "index.html",
6 | "scripts": {
7 | "test": "mocha tests/e2e.test.js",
8 | "start": "http-server -a localhost -p 3000 -P http://localhost:3000?"
9 | },
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "lit-html": "^1.3.0",
14 | "page": "^1.11.6"
15 | },
16 | "devDependencies": {
17 | "http-server": "^0.12.3",
18 | "playwright-chromium": "^1.9.2",
19 | "mocha": "^8.3.2",
20 | "chai": "^4.3.4"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/10 Exam Prep 2/CarTube/requests/backendAPI.js:
--------------------------------------------------------------------------------
1 | const origin = 'http://localhost:3030'
2 |
3 | const createOptions = (method = 'GET', body) => {
4 | const options = {
5 | method,
6 | headers: {},
7 | }
8 | const token = sessionStorage.getItem('accessToken')
9 |
10 | if (token) {
11 | options.headers['X-Authorization'] = token
12 | }
13 | if (body) {
14 | options.headers['Content-Type'] = 'application/json'
15 | options.body = JSON.stringify(body)
16 | }
17 |
18 | return options
19 | }
20 |
21 | const request = async (uri, options) => {
22 | const URI = uri[0] === '/' ? uri : `/${uri}`
23 | let response = {}
24 |
25 | try {
26 | response = await fetch(`${origin}${URI}`, options)
27 | } catch (e) {
28 | throw new Error(e)
29 | }
30 |
31 | if (! response.ok) {
32 | throw new Error(response.status)
33 | }
34 |
35 | try {
36 | return await response.json()
37 | } catch (e) {
38 | return response
39 | }
40 | }
41 |
42 | const fetches = {
43 | get: async (uri) => await request(uri, createOptions()),
44 | post: async (uri, body) => await request(uri, createOptions('POST', body)),
45 | put: async (uri, body) => await request(uri, createOptions('PUT', body)),
46 | delete: async (uri) => await request(uri, createOptions('DELETE')),
47 | }
48 |
49 | export { fetches }
--------------------------------------------------------------------------------
/10 Exam Prep 2/CarTube/requests/requests.js:
--------------------------------------------------------------------------------
1 | import { fetches } from './backendAPI.js'
2 |
3 | const user = {
4 | register: (body) => fetches.post('users/register', body),
5 | login: (body) => fetches.post('users/login', body),
6 | logout: () => fetches.get('users/logout'),
7 | }
8 |
9 | const cars = {
10 | getAllListings: () => fetches.get('/data/cars?sortBy=_createdOn%20desc'),
11 | createListing: ({
12 | brand,
13 | model,
14 | description,
15 | year,
16 | imageUrl,
17 | price
18 | }
19 | ) => fetches.post('/data/cars', {
20 | brand,
21 | model,
22 | description,
23 | year,
24 | imageUrl,
25 | price
26 | }
27 | ),
28 | updateListing: (id, {
29 | brand,
30 | model,
31 | description,
32 | year,
33 | imageUrl,
34 | price
35 | }
36 | ) => fetches.put(`/data/cars/${id}`, {
37 | brand,
38 | model,
39 | description,
40 | year,
41 | imageUrl,
42 | price
43 | }
44 | ),
45 | getCurrentListing: (id) => fetches.get(`/data/cars/${id}`),
46 | deleteListing: (id) => fetches.delete(`/data/cars/${id}`),
47 | getSearchResults: (year) => fetches.get(`/data/cars?where=year%3D${year}`),
48 | getUserListings: (userId) => fetches.get(`/data/cars?where=_ownerId%3D%22${userId}%22&sortBy=_createdOn%20desc`),
49 | }
50 |
51 | export { user, cars }
--------------------------------------------------------------------------------
/10 Exam Prep 2/CarTube/src/contextAPI.js:
--------------------------------------------------------------------------------
1 | import { cars, user } from '../requests/requests.js'
2 |
3 | const contextAPI = {
4 | loadAllListings: async (context, next) => {
5 | context.allListings = await cars.getAllListings()
6 | next()
7 | },
8 | storeListing: async (context, next) => {
9 | context.currentListing = await cars.getCurrentListing(context.params.id)
10 | next()
11 | },
12 | storeUserListings: async (context, next) => {
13 | const userId = sessionStorage.getItem('id')
14 | context.userListings = await cars.getUserListings(userId)
15 | next()
16 | },
17 | storeSearchResults: async (context, next) => {
18 | console.log(context)
19 | const year = context.querystring === '' ? '' : context.querystring.split('=')[1] || ''
20 | context.searchResults = year !== '' ? await cars.getSearchResults(year) : []
21 | context.year = year
22 | next()
23 | }
24 | }
25 |
26 | export { contextAPI }
--------------------------------------------------------------------------------
/10 Exam Prep 2/CarTube/src/helper.js:
--------------------------------------------------------------------------------
1 | import { render } from '../node_modules/lit-html/lit-html.js'
2 | import { PageLayout } from '../Components/PageLayout.js'
3 |
4 | const container = document.getElementById('container')
5 | const renderView = (view) => render(PageLayout(view), container)
6 |
7 | const isUserLogged = () => sessionStorage.getItem('accessToken') ? true : false
8 |
9 | const createFormObject = (form) => {
10 | const formData = new FormData(form)
11 |
12 | return Object.fromEntries([...formData.entries()])
13 | }
14 |
15 | const saveUserInStorage = data => {
16 | sessionStorage.setItem('username', data.username)
17 | sessionStorage.setItem('id', data._id)
18 | sessionStorage.setItem('accessToken', data.accessToken)
19 |
20 | return data
21 | }
22 |
23 | const clearForm = (formObj) => {
24 | console.log(Object.values(formObj))
25 | Object.values(formObj).forEach(x => x = '')
26 | }
27 |
28 |
29 | const showNotification = (view, msg) => {
30 | renderView(view(msg))
31 |
32 | setTimeout(() => renderView(view(msg)), 3000)
33 | }
34 |
35 | export {
36 | isUserLogged,
37 | createFormObject,
38 | saveUserInStorage,
39 | renderView,
40 | showNotification,
41 | clearForm
42 | }
--------------------------------------------------------------------------------
/10 Exam Prep 2/CarTube/style/car-listings.css:
--------------------------------------------------------------------------------
1 | .listings {
2 | margin: auto;
3 | text-align: center;
4 | }
5 |
6 | .listing {
7 | box-sizing: border-box;
8 | text-align: left;
9 | display: inline-block;
10 | padding: 30px 50px;
11 | width: 350px;
12 | margin: 15px;
13 | margin-bottom: 50px;
14 | border-radius: 8px;
15 | border: 2px solid black;
16 | }
17 |
18 | .listing h2 {
19 | height: 2.5em;
20 | }
21 |
22 | .preview {
23 | height: 150px;
24 | width: 250px;
25 | text-align: center;
26 | }
27 |
28 | .preview>img {
29 | object-fit: contain;
30 | height: 150px;
31 | width: 250px;
32 | }
33 |
34 | .no-cars {
35 | margin: auto;
36 | font-size: 30px;
37 | text-align: center;
38 | }
39 |
40 | .data-info {
41 | margin-top: 8px;
42 | }
43 |
44 | h3 {
45 | font-size: 20px;
46 | }
47 |
48 | .button-carDetails {
49 | background-color: #4caf50;
50 | border: none;
51 | color: white;
52 | padding: 10px;
53 | width: 100px;
54 | margin: 2px;
55 | text-align: center;
56 | text-decoration: none;
57 | display: inline-block;
58 | font-size: 16px;
59 | }
--------------------------------------------------------------------------------
/10 Exam Prep 2/CarTube/style/listing-details.css:
--------------------------------------------------------------------------------
1 | .listing-props {
2 | margin-top: 16px;
3 | list-style: none;
4 | }
5 |
6 | .listing-props>li {
7 | font-size: 20px;
8 | }
9 | .listing-props>li>span {
10 | font-weight: 600;
11 | display: inline-block;
12 | width: 80px;
13 | }
14 |
15 | .listings-buttons {
16 | text-align: center;
17 | margin: auto;
18 | margin-top: 34px;
19 | }
20 |
21 | .button-list {
22 | cursor: pointer;
23 | background-color: #4caf50;
24 | border: none;
25 | color: white;
26 | margin: auto;
27 | padding: 11px 30px;
28 | text-align: center;
29 | text-decoration: none;
30 | display: inline-block;
31 | font-size: 16px;
32 | }
33 |
34 | .details-info {
35 | max-width: 700px;
36 | margin: auto;
37 | }
38 |
39 | .details-info>img {
40 | display: block;
41 | margin: auto;
42 | margin-bottom: 16px;
43 | max-height: 455px;
44 | }
45 |
46 | .description-para {
47 | margin-top: 16px;
48 | font-size: 16px;
49 | }
--------------------------------------------------------------------------------
/10 Exam Prep 2/CarTube/style/login-register.css:
--------------------------------------------------------------------------------
1 | /* Add padding to containers */
2 |
3 | .container {
4 | background-color: white;
5 | padding: 16px;
6 | text-align: center;
7 | width: 500px;
8 | margin: auto;
9 | }
10 |
11 | /* Full-width input fields */
12 |
13 | input[type="text"], input[type="number"], input[type="password"] {
14 | padding: 15px;
15 | margin: 5px 0 22px 0;
16 | width: 100%;
17 | border: none;
18 | background: #f1f1f1;
19 | box-sizing: border-box;
20 | }
21 |
22 | input[type="text"]:focus, input[type="number"]:focus, input[type="password"]:focus {
23 | background-color: #ddd;
24 | outline: none;
25 | }
26 |
27 | /* Overwrite default styles of hr */
28 |
29 | hr {
30 | border: 1px solid #f1f1f1;
31 | margin-bottom: 25px;
32 | }
33 |
34 | /* Set a style for the submit/register button */
35 |
36 | .registerbtn {
37 | background-color: #4CAF50;
38 | color: white;
39 | padding: 16px 20px;
40 | margin: 8px 0;
41 | border: none;
42 | cursor: pointer;
43 | opacity: 0.9;
44 | }
45 |
46 | .registerbtn:hover {
47 | opacity: 1;
48 | }
49 |
50 | /* Add a blue text color to links */
51 |
52 | a {
53 | color: dodgerblue;
54 | }
55 |
56 | /* Set a grey background color and center the text of the "sign in" section */
57 |
58 | .signin {
59 | padding: 16px;
60 | background-color: #f1f1f1;
61 | text-align: center;
62 | }
--------------------------------------------------------------------------------
/10 Exam Prep 2/CarTube/style/navigation.css:
--------------------------------------------------------------------------------
1 | nav {
2 | background-color: #333;
3 | overflow: hidden;
4 | }
5 |
6 | #profile, #guest {
7 | float: right;
8 | }
9 |
10 | /* Style the links inside the navigation bar */
11 |
12 | nav a {
13 | float: left;
14 | color: #f2f2f2;
15 | text-align: center;
16 | padding: 14px 16px;
17 | text-decoration: none;
18 | font-size: 17px;
19 | }
20 |
21 | /* Change the color of links on hover */
22 |
23 | nav a:hover {
24 | background-color: #ddd;
25 | color: black;
26 | }
27 |
28 | /* Add a color to the active/current link */
29 |
30 | nav a.active {
31 | background-color: #4CAF50;
32 | color: white;
33 | }
--------------------------------------------------------------------------------
/10 Exam Prep 2/CarTube/style/search.css:
--------------------------------------------------------------------------------
1 | .searchCars input {
2 | display: block;
3 | width: 400px;
4 | margin-right: auto;
5 | margin-left: auto;
6 | }
7 |
8 | .searchCars {
9 | width: 100vw;
10 | position: relative;
11 |
12 | display: flex;
13 | flex-flow: column wrap;
14 | align-items: center;
15 | margin-bottom: 20px;
16 | }
17 |
--------------------------------------------------------------------------------
/10 Exam Prep 2/CarTube/style/site.css:
--------------------------------------------------------------------------------
1 | * {
2 | padding: 0;
3 | margin: 0;
4 | }
5 |
6 | body {
7 | font-family: 'Lato', sans-serif;
8 | }
9 |
10 | footer {
11 | position: absolute;
12 | bottom: 0;
13 | left: 0;
14 | right: 0;
15 | background-color: #333333;
16 | color: white;
17 | text-align: center;
18 | padding: 16px;
19 | }
20 |
21 | #container {
22 | box-sizing: border-box;
23 | min-height: 100vh;
24 | position: relative;
25 | padding-bottom: 64px;
26 | }
--------------------------------------------------------------------------------
/10 Exam Prep 2/CarTube/style/styles.css:
--------------------------------------------------------------------------------
1 | @import "site.css";
2 | @import "welcome.css";
3 | @import "car-listings.css";
4 | @import "login-register.css";
5 | @import "navigation.css";
6 | @import "listing-details.css";
7 | @import "search.css";
8 |
9 |
--------------------------------------------------------------------------------
/10 Exam Prep 2/CarTube/style/welcome.css:
--------------------------------------------------------------------------------
1 | #main {
2 | background-color: white;
3 | font-family: 'Lato', sans-serif;
4 | }
5 |
6 | h1 {
7 | text-align: center;
8 | margin-top: 30px;
9 | font-size: 50px;
10 | }
11 |
12 | h2 {
13 | text-align: center;
14 | }
15 |
16 | .button {
17 | background-color: #4CAF50;
18 | /* Green */
19 | border: none;
20 | color: white;
21 | padding: 15px 32px;
22 | text-align: center;
23 | text-decoration: none;
24 | display: inline-block;
25 | font-size: 16px;
26 | margin: 5px;
27 | }
28 |
29 | #welcome-container {
30 | width: 50%;
31 | margin: auto;
32 | text-align: center;
33 | }
34 |
35 | .hero {
36 | margin: 40px;
37 | max-width: 240px;
38 | }
39 |
40 | #button-div {
41 | padding-top: 10px;
42 | width: 37%;
43 | margin: auto;
44 | display: flex;
45 | }
--------------------------------------------------------------------------------
/10 Exam Prep 2/CarTube/tests/mock-data.json:
--------------------------------------------------------------------------------
1 | {
2 | "users": [
3 | {
4 | "_id": "0001",
5 | "username": "Peter",
6 | "password": "123456",
7 | "accessToken": "AAAA"
8 | },
9 | {
10 | "_id": "0002",
11 | "username": "John",
12 | "password": "123456",
13 | "accessToken": "BBBB"
14 | }
15 | ],
16 | "catalog": [
17 | {
18 | "_id": "1001",
19 | "_ownerId": "0001",
20 | "brand": "brand1",
21 | "model": "model1",
22 | "description": "description1",
23 | "year": 2011,
24 | "imageUrl": "/images/1.png",
25 | "price": 1000
26 | },
27 | {
28 | "_id": "1002",
29 | "_ownerId": "0001",
30 | "brand": "brand2",
31 | "model": "model2",
32 | "description": "description2",
33 | "year": 2012,
34 | "imageUrl": "/images/2.png",
35 | "price": 2000
36 | },
37 | {
38 | "_id": "1003",
39 | "_ownerId": "0002",
40 | "brand": "brand3",
41 | "model": "model3",
42 | "description": "description3",
43 | "year": 2013,
44 | "imageUrl": "/images/3.png",
45 | "price": 3000
46 | }
47 | ]
48 | }
--------------------------------------------------------------------------------
/applications_javascript.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silverster0113/JS-Applications-February-2021/878573b5a22907f709083ce25dfcd511dcb78cce/applications_javascript.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "js-applications",
3 | "version": "1.0.0",
4 | "description": "Major Part of JS Applications Feb 2021 Lab and Exercises as well as Exams.",
5 | "main": "index.js",
6 | "dependencies": {
7 | "http-server": "^0.12.3",
8 | "lite-server": "^2.6.1",
9 | "sloc": "^0.2.1"
10 | },
11 | "devDependencies": {
12 | "chai": "^4.3.4",
13 | "lit-html": "^1.3.0",
14 | "mocha": "^8.3.2",
15 | "playwright-chromium": "^1.9.2"
16 | },
17 | "scripts": {
18 | "test": "echo \"Error: no test specified\" && exit 1"
19 | },
20 | "repository": {
21 | "type": "git",
22 | "url": "git+https://github.com/Sineastra/JS-Applications-February-2021.git"
23 | },
24 | "author": "",
25 | "license": "ISC",
26 | "bugs": {
27 | "url": "https://github.com/Sineastra/JS-Applications-February-2021/issues"
28 | },
29 | "homepage": "https://github.com/Sineastra/JS-Applications-February-2021#readme",
30 | "keywords": []
31 | }
32 |
--------------------------------------------------------------------------------