Current (non-working-frontend) state
This commit is contained in:
		| @@ -85,7 +85,6 @@ model itemCategory { | ||||
|   Item        Item[] | ||||
| } | ||||
|  | ||||
| /// TODO: Add relationship to StorageUnit, Item and if necessary to StorageLocation. | ||||
| model contactInfo { | ||||
|   id          Int         @id @default(autoincrement()) | ||||
|   type        contactType @default(person) | ||||
| @@ -101,6 +100,7 @@ model contactInfo { | ||||
|   Item        Item[] | ||||
| } | ||||
|  | ||||
| /// TODO: Allow multiple types to be used? | ||||
| enum contactType { | ||||
|   storageUnit | ||||
|   owner | ||||
|   | ||||
| @@ -1,8 +1,80 @@ | ||||
| <%~ E.includeFile("partials/head.eta.html", {"title": "Items"}) %> <%~ E.includeFile("partials/controls.eta.html", {"active": "Items"}) %> | ||||
|  | ||||
| <div class="modal fade" id="itemModifyModal" tabindex="-1" aria-labelledby="itemModifyModal" aria-hidden="true"> | ||||
| 	<div class="modal-dialog"> | ||||
| 		<div class="modal-content"> | ||||
| 			<div class="modal-header"> | ||||
| 				<h1 class="modal-title fs-5" id="itemModifyModalLabel">Edit a item</h1> | ||||
| 				<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> | ||||
| 			</div> | ||||
| 			<form class="frontendForm" method="patch" data-target="/api/v1/items" id="CategoryModalForm"> | ||||
| 				<div class="modal-body"> | ||||
| 					<div class="mb-3"> | ||||
| 						<label for="itemModifyModalName" class="form-label">Name</label> | ||||
| 						<input type="text" class="form-control" id="itemModifyModalName" name="name" required /> | ||||
| 						<div id="itemModifyModalNameText" class="form-text">This name should be unqiue.</div> | ||||
| 					</div> | ||||
| 					<div class="mb-3"> | ||||
| 						<label for="itemModifyModalComment" class="form-label">Comment</label> | ||||
| 						<input type="text" class="form-control" id="itemModifyModalComment" name="comment" /> | ||||
| 						<div id="itemModifyModalDescText" class="form-text">Optional</div> | ||||
| 					</div> | ||||
| 					<div class="mb-3"> | ||||
| 						<label for="itemModifyModalStorageLocation" class="form-label">Select a storage location</label> | ||||
| 						<select class="form-select" id="itemModifyModalStorageLocation" name="storageLocation" required> | ||||
| 							<option value="undefined"><i>Do not assign a storage location</i></option> | ||||
| 							<% it.storeLocs.forEach(function(locs){ %> | ||||
| 							<option value="<%= locs.id %>"><%= locs.name %></option> | ||||
| 							<% }) %> | ||||
| 						</select> | ||||
|  | ||||
| 						<div id="itemModifyModalStorageLocationText" class="form-text">You have to create a storage location beforehand.</div> | ||||
| 					</div> | ||||
| 					<div class="mb-3"> | ||||
| 						<label for="itemModifyModalAmount" class="form-label">Amount</label> | ||||
| 						<input type="number" min="0" class="form-control" id="itemModifyModalAmount" name="amount" /> | ||||
| 					</div> | ||||
| 					<div class="mb-3"> | ||||
| 						<label for="itemModifyModalSKU" class="form-label">SKU</label> | ||||
| 						<input type="text" class="form-control" id="itemModifyModalSKU" name="sku" /> | ||||
| 						<div id="itemModifyModalSKUText" class="form-text">Optional</div> | ||||
| 					</div> | ||||
| 					<div class="mb-3"> | ||||
| 						<label for="itemModifyModalManuf" class="form-label">Manufacturer</label> | ||||
| 						<input type="number" min="0" class="form-control" id="itemModifyModalManuf" name="manufacturer" /> | ||||
| 						<div id="itemModifyModalSKUText" class="form-text">Optional</div> | ||||
| 					</div> | ||||
| 					<div class="mb-3"> | ||||
| 						<label for="itemModifyModalCategory" class="form-label">Select a category</label> | ||||
| 						<select class="form-select" id="itemModifyModalCategory" name="category" required> | ||||
| 							<option value="undefined"><i>Do not assign a category</i></option> | ||||
| 							<% it.categories.forEach(function(cat){ %> | ||||
| 							<option value="<%= cat.id %>"><%= cat.name %></option> | ||||
| 							<% }) %> | ||||
| 						</select> | ||||
|  | ||||
| 						<div id="storageLocationModalLocationText" class="form-text">You have to create a storage location beforehand.</div> | ||||
| 						<input type="hidden" id="storageLocationModalIdHidden" name="id" /> | ||||
| 					</div> | ||||
| 					<input type="text" id="itemModifyModalId" name="id" hidden /> | ||||
| 				</div> | ||||
| 				<div class="modal-footer"> | ||||
| 					<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button> | ||||
| 					<button type="submit" class="btn btn-primary">Save changes</button> | ||||
| 				</div> | ||||
| 			</form> | ||||
| 		</div> | ||||
| 	</div> | ||||
| </div> | ||||
|  | ||||
| <!-- TODO: Center table content --> | ||||
| <h1>Items</h1> | ||||
| <div class="container"> | ||||
| 	<div class="row"> | ||||
| 		<div class="col-12"> | ||||
| 			<a href="/settings/category/new" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#itemModifyModal" onclick="primeCreateNew()">Create new item</a> | ||||
| 		</div> | ||||
| 	</div> | ||||
| 	<table class="table align-middle"> | ||||
| 		<thead> | ||||
| 			<tr> | ||||
| @@ -39,7 +111,7 @@ | ||||
| 			<% }) %> | ||||
| 		</tbody> | ||||
| 	</table> | ||||
| 	<br> | ||||
| 	<br /> | ||||
| 	<% if(it.maxPages > 1) { %> | ||||
| 	<nav aria-label="Page selector"> | ||||
| 		<ul class="pagination justify-content-center"> | ||||
|   | ||||
| @@ -1,19 +1,10 @@ | ||||
| <header class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0 shadow"> | ||||
| 	<a class="navbar-brand col-md-3 col-lg-2 me-0 px-3 test-white-50" onclick="doTheConfetti()">AssetFlow</a> | ||||
| 	<script> | ||||
| 		function randomInRange(min, max) { | ||||
| 			return Math.random() * (max - min) + min; | ||||
| 		} | ||||
|  | ||||
| 		function doTheConfetti() { | ||||
| 			confetti({ | ||||
| 				angle: randomInRange(40, 150), | ||||
| 				spread: randomInRange(50, 100), | ||||
| 				particleCount: randomInRange(50, 150), | ||||
| 				origin: { y: 0.6 } | ||||
| 			}); | ||||
| 		} | ||||
| 	</script> | ||||
| <nav class="navbar navbar-expand-lg bg-body-tertiary sticky-top" style="z-index: 9999"> | ||||
| 	<div class="container-fluid"> | ||||
| 		<img src="/logo/Design_icon.svg" height="10%"> | ||||
| 		<a class="navbar-brand" href="#" onclick="doTheConfetti()">AssetFlow</a> | ||||
| 		<!-- 		<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> | ||||
| 			<span class="navbar-toggler-icon"></span> | ||||
| 		</button> --> | ||||
| 		<button | ||||
| 			class="navbar-toggler position-absolute d-md-none collapsed" | ||||
| 			type="button" | ||||
| @@ -24,16 +15,18 @@ | ||||
| 			aria-label="Toggle navigation"> | ||||
| 			<span class="navbar-toggler-icon"></span> | ||||
| 		</button> | ||||
| 		<div class="" id="navbarSupportedContent"> | ||||
| 			<ul class="navbar-nav me-auto mb-2 mb-lg-0"> | ||||
| 				 | ||||
| 	<input class="form-control form-control-dark w-100 bg-secondary" type="text" placeholder="Search" aria-label="Search" id="SearchBox" /> | ||||
| 			</ul> | ||||
| 			<form class="d-flex" role="search"> | ||||
| 				<input class="form-control me-2" type="search" placeholder="Search" aria-label="Search" id="SearchBox" /> | ||||
| 				<div class="autocomplete-items bg-secondary w-75 border-primary me-5 p-2" id="autocomplete-items" style="left: 16.7%"></div> | ||||
| 			</form> | ||||
| 		</div> | ||||
| 	</div> | ||||
| </nav> | ||||
|  | ||||
| 	<div class="navbar-nav"> | ||||
| 		<div class="nav-item text-nowrap"> | ||||
| 			<a class="nav-link px-3" id="logoutButton">Sign out</a> | ||||
| 		</div> | ||||
| 	</div> | ||||
| </header> | ||||
| <div class="toast" role="alert" aria-live="assertive" aria-atomic="true" id="masterToast" style="z-index: 2000"> | ||||
| 	<div class="d-flex"> | ||||
| 		<div class="toast-body">Hello, world! This is a toast message.</div> | ||||
| @@ -49,37 +42,6 @@ | ||||
| 	</div> | ||||
| </div> | ||||
|  | ||||
| <div class="toast-container position-fixed bottom-0 end-0 p-3"> | ||||
| 	<div id="liveToast" class="toast" role="alert" aria-live="assertive" aria-atomic="true"> | ||||
| 		<div class="toast-header"> | ||||
| 			<strong class="me-auto">Notification</strong> | ||||
| 			<small>Just now</small> | ||||
| 			<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button> | ||||
| 		</div> | ||||
| 		<div class="toast-body" id="toastText">The button you just pressed is very useless.</div> | ||||
| 	</div> | ||||
| </div> | ||||
| <script> | ||||
| 	let texti = 0; | ||||
| 	alltexts = ['Nope, still useless', 'Stop pressing me!', 'There are NO USERS!', 'Please stop.', 'PLEASE!', 'Do you want an achivment or what?', 'This is not a game, please stop.', 'This is not the stanley parable.']; | ||||
| 	const toastLiveExample = document.getElementById('liveToast'); | ||||
| 	const logoutButton = document.getElementById('logoutButton'); | ||||
| 	logoutButton.addEventListener('click', () => { | ||||
| 		toastFunction(); | ||||
| 		texti++; | ||||
| 		if (texti >= alltexts.length) texti = 0; | ||||
| 	}); | ||||
|  | ||||
| 	function toastFunction() { | ||||
| 		const toastBootstrap = bootstrap.Toast.getOrCreateInstance(toastLiveExample); | ||||
| 		toastBootstrap.show(); | ||||
| 		setTimeout(function () { | ||||
| 			toastBootstrap.hide(); | ||||
| 			document.getElementById('toastText').innerHTML = alltexts[texti]; | ||||
| 		}, 3000); | ||||
| 	} | ||||
| </script> | ||||
|  | ||||
| <div class="container-fluid"> | ||||
| 	<div class="row"> | ||||
| 		<nav id="sidebarMenu" class="col-md-3 col-lg-2 d-md-block sidebar collapse"> | ||||
| @@ -187,17 +149,17 @@ | ||||
| 					} | ||||
| 					modeLight.addEventListener('click', () => { | ||||
| 						localStorage.setItem('bs.theme', 'light'); | ||||
| 						updateColorMode() | ||||
| 						updateColorMode(); | ||||
| 						//document.documentElement.setAttribute('data-bs-theme', 'light'); | ||||
| 					}); | ||||
| 					modeAuto.addEventListener('click', () => { | ||||
| 						localStorage.setItem('bs.theme', 'auto'); | ||||
| 						updateColorMode() | ||||
| 						updateColorMode(); | ||||
| 						//document.documentElement.setAttribute('data-bs-theme', 'auto'); | ||||
| 					}); | ||||
| 					modeDark.addEventListener('click', () => { | ||||
| 						localStorage.setItem('bs.theme', 'dark'); | ||||
| 						updateColorMode() | ||||
| 						updateColorMode(); | ||||
| 						//document.documentElement.setAttribute('data-bs-theme', 'dark'); | ||||
| 					}); | ||||
| 				</script> | ||||
|   | ||||
| @@ -8,13 +8,13 @@ | ||||
| 		<title>AssetFlow - <%= it.title %></title> | ||||
| 		<meta name="author" content="[Project-Name-Here]" /> | ||||
|  | ||||
| 		<link rel="icon" href="/favicon.ico" /> | ||||
| 		<link rel="icon" href="/favicon.svg" type="image/svg+xml" /> | ||||
| 		<!--<link rel="icon" href="/favicon.ico" />--> | ||||
| 		<link rel="icon" href="/logo/Design_icon.svg" type="image/svg+xml" /> | ||||
|  | ||||
| 		<script src="/js/handleColorMode.js"></script> | ||||
| 		<script src="/js/toastHandler.js"></script> | ||||
| 		<script src="/static/jquery/dist/jquery.min.js"></script> | ||||
| 		<script src="/js/toastHandler.js"></script> | ||||
| 		<script src="/js/confettiHeader.js"></script> | ||||
| 		<link href="/static/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet" /> | ||||
| 		<link href="/static/bootstrap-icons/font/bootstrap-icons.css" rel="stylesheet" /> | ||||
| 		<link href="/css/dashboard.css" rel="stylesheet" /> | ||||
|   | ||||
| @@ -2,14 +2,15 @@ import express from 'express'; | ||||
|  | ||||
| // Route imports | ||||
| import testRoute from './test.js'; | ||||
| import itemRoute from './items.js'; | ||||
| import categoryRoute from './categories.js'; | ||||
| import storageUnitRoute from './storageUnits.js'; | ||||
| import storageLocationRoute from './storageLocations.js'; | ||||
|  | ||||
|  | ||||
| // Router base is '/api/v1' | ||||
| const Router = express.Router({ strict: false }); | ||||
|  | ||||
| Router.route('/items').get(itemRoute.get).post(itemRoute.post).patch(itemRoute.patch).delete(itemRoute.del); | ||||
| Router.route('/categories').get(categoryRoute.get).post(categoryRoute.post).patch(categoryRoute.patch).delete(categoryRoute.del); | ||||
| // TODO: Migrate routes to lowercase. | ||||
| Router.route('/storageUnits').get(storageUnitRoute.get).post(storageUnitRoute.post).patch(storageUnitRoute.patch).delete(storageUnitRoute.del); | ||||
|   | ||||
							
								
								
									
										212
									
								
								src/routes/api/v1/items.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										212
									
								
								src/routes/api/v1/items.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,212 @@ | ||||
| import { Request, Response } from 'express'; | ||||
| import { prisma, __path, log } from '../../../index.js'; | ||||
|  | ||||
| // Get item. | ||||
| function get(req: Request, res: Response) { | ||||
| 	if (req.query.getAll === undefined) { | ||||
| 		// Check if required fields are present. | ||||
| 		if (!req.query.id) { | ||||
| 			res.status(400).render(__path + '/src/frontend/errors/400.eta.html'); | ||||
| 			return; | ||||
| 		} | ||||
| 		prisma.item | ||||
| 			.findUnique({ | ||||
| 				where: { | ||||
| 					id: parseInt(req.query.id.toString()) | ||||
| 				}, | ||||
| 				// Get contactInfo, category, storageLocation( storageUnit<contactInfo> ) from relations. | ||||
| 				include: { | ||||
| 					contactInfo: true, | ||||
| 					category: true, | ||||
| 					storageLocation: { | ||||
| 						include: { | ||||
| 							storageUnit: { | ||||
| 								include: { | ||||
| 									contactInfo: true | ||||
| 								} | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			}) | ||||
| 			.then((items) => { | ||||
| 				if (items) { | ||||
| 					res.status(200).json(JSON.stringify(items)); | ||||
| 				} else { | ||||
| 					res.status(410).json({ error: 'it seems that there is no item present' }); | ||||
| 				} | ||||
| 			}) | ||||
| 			.catch((err) => { | ||||
| 				console.error(err); | ||||
| 				res.status(500).render(__path + '/src/frontend/errors/dbError.eta.html', { error: err }); | ||||
| 			}); | ||||
| 	} else { | ||||
| 		prisma.item | ||||
| 			.findMany({ | ||||
| 				// Get contactInfo, category, storageLocation( storageUnit<contactInfo> ) from relations. | ||||
| 				include: { | ||||
| 					contactInfo: true, | ||||
| 					category: true, | ||||
| 					storageLocation: { | ||||
| 						include: { | ||||
| 							storageUnit: { | ||||
| 								include: { | ||||
| 									contactInfo: true | ||||
| 								} | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			}) | ||||
| 			.then((items) => { | ||||
| 				if (items) { | ||||
| 					res.status(200).json(JSON.stringify(items)); | ||||
| 				} else { | ||||
| 					res.status(410).json({ error: 'item does not exist' }); | ||||
| 				} | ||||
| 			}) | ||||
| 			.catch((err) => { | ||||
| 				console.error(err); | ||||
| 				res.status(500).render(__path + '/src/frontend/errors/dbError.eta.html', { error: err }); | ||||
| 			}); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Create item. | ||||
| function post(req: Request, res: Response) { | ||||
| 	/*  | ||||
| 	// Check if required fields are present. | ||||
| 	if (!req.body.name) { | ||||
| 		res.status(400).render(__path + '/src/frontend/errors/400.eta.html'); | ||||
| 		return; | ||||
| 	} | ||||
| 	// Create storageLocation with existing storageUnit. | ||||
| 	prisma.storageLocation | ||||
| 		.create({ | ||||
| 			data: { | ||||
| 				name: req.body.name, | ||||
| 				storageUnitId: parseInt(req.body.storageUnitId) || undefined | ||||
| 			}, | ||||
| 			select: { | ||||
| 				id: true | ||||
| 			} | ||||
| 		}) | ||||
| 		.then((data) => { | ||||
| 			res.status(201).json({ status: 'created', id: data.id }); | ||||
| 		}) | ||||
| 		.catch((err) => { | ||||
| 			// Check if an entry already exists. | ||||
| 			if (err.code === 'P2002') { | ||||
| 				// P2002 -> "Unique constraint failed on the {constraint}" | ||||
| 				// https://www.prisma.io/docs/reference/api-reference/error-reference | ||||
| 				res.status(409).json({ error: 'storageLocation already exists.' }); | ||||
| 			} else if (err.code == 'P2003') { | ||||
| 				// P2003 -> "Foreign key constraint failed on the field: {field_name}" | ||||
| 				// https://www.prisma.io/docs/reference/api-reference/error-reference | ||||
| 				res.status(404).json({ error: 'specified storageUnitId does not exist' }); | ||||
| 			} else { | ||||
| 				log.db.error(err); | ||||
| 				res.status(500).render(__path + '/src/frontend/errors/dbError.eta.html', { error: err }); | ||||
| 			} | ||||
| 		}); | ||||
| */ | ||||
| } | ||||
|  | ||||
| // Update storageLocation. -> Only existing contactInfo. | ||||
| async function patch(req: Request, res: Response) { | ||||
| 	/*  | ||||
| 	// Check if required fields are present. | ||||
| 	if (!req.body.id || !req.body.name || !req.body.storageUnitId) { | ||||
| 		res.status(400).render(__path + '/src/frontend/errors/400.eta.html'); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	// Check if the storageLocation id exists. If not return 410 Gone. | ||||
| 	try { | ||||
| 		const result = await prisma.storageLocation.findUnique({ | ||||
| 			where: { | ||||
| 				id: parseInt(req.body.id) | ||||
| 			} | ||||
| 		}); | ||||
|  | ||||
| 		if (result === null) { | ||||
| 			res.status(404).json({ error: 'storageLocation does not exist.' }); | ||||
| 			return; | ||||
| 		} | ||||
| 	} catch (err) { | ||||
| 		log.db.error(err); | ||||
| 		res.status(500).render(__path + '/src/frontend/errors/dbError.eta.html', { error: err }); | ||||
| 	} | ||||
|  | ||||
| 	prisma.storageLocation | ||||
| 		.update({ | ||||
| 			where: { | ||||
| 				id: parseInt(req.body.id) | ||||
| 			}, | ||||
| 			data: { | ||||
| 				name: req.body.name, | ||||
| 				storageUnitId: parseInt(req.body.storageUnitId) || undefined | ||||
| 			} | ||||
| 		}) | ||||
| 		.then(() => { | ||||
| 			res.status(201).json({ status: 'updated' }); | ||||
| 		}) | ||||
| 		.catch((err) => { | ||||
| 			// Check if an entry already exists. | ||||
| 			if (err.code === 'P2002') { | ||||
| 				// P2002 -> "Unique constraint failed on the {constraint}" | ||||
| 				// https://www.prisma.io/docs/reference/api-reference/error-reference | ||||
| 				res.status(409).json({ error: 'storageLocation already exists.' }); | ||||
| 			} else if (err.code == 'P2003') { | ||||
| 				// P2003 -> "Foreign key constraint failed on the field: {field_name}" | ||||
| 				// https://www.prisma.io/docs/reference/api-reference/error-reference | ||||
| 				res.status(404).json({ error: 'specified storageUnitId does not exist' }); | ||||
| 			} else { | ||||
| 				log.db.error(err); | ||||
| 				res.status(500).render(__path + '/src/frontend/errors/dbError.eta.html', { error: err }); | ||||
| 			} | ||||
| 		}); | ||||
| */ | ||||
| } | ||||
|  | ||||
| // Delete item. | ||||
| async function del(req: Request, res: Response) { | ||||
| 	// Check if required fields are present. | ||||
| 	if (!req.body.id) { | ||||
| 		res.status(400).render(__path + '/src/frontend/errors/400.eta.html'); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	// Does the id exist? If not return 410 Gone. | ||||
| 	try { | ||||
| 		const result = await prisma.item.findUnique({ | ||||
| 			where: { | ||||
| 				id: parseInt(req.body.id) | ||||
| 			} | ||||
| 		}); | ||||
|  | ||||
| 		if (result === null) { | ||||
| 			res.status(410).json({ error: 'item does not exist' }); | ||||
| 			return; | ||||
| 		} | ||||
| 	} catch (err) { | ||||
| 		log.db.error(err); | ||||
| 		res.status(500).render(__path + '/src/frontend/errors/dbError.eta.html', { error: err }); | ||||
| 	} | ||||
|  | ||||
| 	prisma.item | ||||
| 		.delete({ | ||||
| 			where: { | ||||
| 				id: parseInt(req.body.id) | ||||
| 			} | ||||
| 		}) | ||||
| 		.then(() => { | ||||
| 			res.status(200).json({ status: 'deleted' }); | ||||
| 		}) | ||||
| 		.catch((err) => { | ||||
| 			log.db.error(err); | ||||
| 			res.status(500).render(__path + '/src/frontend/errors/dbError.eta.html', { error: err }); | ||||
| 		}); | ||||
| } | ||||
|  | ||||
| export default { get, post, patch, del }; | ||||
| @@ -25,7 +25,11 @@ async function get(req: Request, res: Response) { | ||||
| 	prisma.item | ||||
| 		.findMany({ skip: (page - 1) * takeSize, take: takeSize }) // Skip the amount of items per page times the page number minus 1; skip has to be (page-1)*takeSize because skip is 0 indexed | ||||
| 		.then((items) => { | ||||
| 			res.render(__path + '/src/frontend/items.eta.html', { items: items, currentPage: page, maxPages: pageSize }); | ||||
| 			prisma.storageLocation.findMany({}).then((locations) => { | ||||
| 				prisma.itemCategory.findMany({}).then((categories) => { | ||||
| 					res.render(__path + '/src/frontend/items.eta.html', { items: items, currentPage: page, maxPages: pageSize, storeLocs: locations, categories: categories }); | ||||
| 				}); | ||||
| 			}); | ||||
| 		}) | ||||
| 		.catch((err) => { | ||||
| 			console.error(err); | ||||
|   | ||||
| @@ -8,7 +8,7 @@ body { | ||||
|  | ||||
| .sidebar { | ||||
| 	position: fixed; | ||||
| 	top: 0; | ||||
| 	top: 0.5rem; | ||||
|  | ||||
| 	bottom: 0; | ||||
| 	left: 0; | ||||
| @@ -16,12 +16,12 @@ body { | ||||
| 	padding: 48px 0 0; /* Height of navbar */ | ||||
| 	box-shadow: inset -1px 0 0 rgba(0, 0, 0, 0.1); | ||||
| } | ||||
|  | ||||
| /* | ||||
| @media (max-width: 767.98px) { | ||||
| 	.sidebar { | ||||
| 		top: 5rem; | ||||
| 	} | ||||
| } | ||||
| }*/ | ||||
|  | ||||
| .sidebar-sticky { | ||||
| 	position: relative; | ||||
| @@ -63,7 +63,6 @@ body { | ||||
|  | ||||
| /* | ||||
|  * Navbar | ||||
|  */ | ||||
|  | ||||
| .navbar-brand { | ||||
| 	padding-top: 0.75rem; | ||||
| @@ -71,7 +70,7 @@ body { | ||||
| 	font-size: 1rem; | ||||
| 	box-shadow: inset -1px 0 0 rgba(0, 0, 0, 0.25); | ||||
| } | ||||
|  | ||||
| */ | ||||
| .navbar .navbar-toggler { | ||||
| 	top: 0.25rem; | ||||
| 	right: 1rem; | ||||
|   | ||||
							
								
								
									
										12
									
								
								static/js/confettiHeader.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								static/js/confettiHeader.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| function randomInRange(min, max) { | ||||
| 	return Math.random() * (max - min) + min; | ||||
| } | ||||
|  | ||||
| function doTheConfetti() { | ||||
| 	confetti({ | ||||
| 		angle: 100, | ||||
| 		spread: randomInRange(70, 120), | ||||
| 		particleCount: randomInRange(100, 200), | ||||
| 		origin: { y: 0.6 } | ||||
| 	}); | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								static/logo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								static/logo.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 71 KiB | 
							
								
								
									
										1
									
								
								static/logo/Design_Logo_Black.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								static/logo/Design_Logo_Black.svg
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| After Width: | Height: | Size: 25 KiB | 
							
								
								
									
										1
									
								
								static/logo/Design_Logo_white.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								static/logo/Design_Logo_white.svg
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| After Width: | Height: | Size: 25 KiB | 
							
								
								
									
										1
									
								
								static/logo/Design_icon.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								static/logo/Design_icon.svg
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| After Width: | Height: | Size: 22 KiB | 
		Reference in New Issue
	
	Block a user