Introduction
This article is the fourth part of a 10 part series that I posted as a tutorial for the readers. I will go through step-by-step going over how I created a link shortener app from scratch.
In this post, we will add a front-end to our app and start designing our app.
We will use a template called SkyDash which was developed by the amazing team of BootstrapDash so special thanks to them for making this template free and public.
Dependencies installation
Lets install the new dependencies for this part.
npm i express body-parser ejs
This will allow us to create the building block of our server and render our front end in an ejs engine.
Also as mentioned, we will be using a template, so create a new folder called template and run the following command for us to be able to have access to our template easily
git clone https://github.com/BootstrapDash/skydash-free-bootstrap-admin-template
This should download the template into the newly created template folder. Normally we wll never need to touch this part, since it will just be a reference point for us to create our own front-end.
Copy necesarry files
Create a new folder called public from your root folder, and from the template folder of the SkyDash, let us now copy the css, fonts, js, partials, scss, images, and vendors folders, so your folder now should look like this.
This allow us to add the styling from the template with ease.
Create a frame for each of our pages
Normally, in my projects what, I would like to do before writing the product is to create a clean frame template for each of my pages, therefore, whenever I want to add a new page, I can just copy and add the content separately.
For example, as we can see originally the index.html renders a dashboard such as below
This has a lot of elements that I will not use, so I need to clean to get the following view
This is the html code of the above pages:
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>KECIL APP</title>
<!-- plugins:css -->
<link rel="stylesheet" href="vendors/feather/feather.css">
<link rel="stylesheet" href="vendors/ti-icons/css/themify-icons.css">
<link rel="stylesheet" href="vendors/css/vendor.bundle.base.css">
<!-- endinject -->
<!-- Plugin css for this page -->
<link rel="stylesheet" href="vendors/datatables.net-bs4/dataTables.bootstrap4.css">
<link rel="stylesheet" href="vendors/ti-icons/css/themify-icons.css">
<link rel="stylesheet" type="text/css" href="js/select.dataTables.min.css">
<!-- End plugin css for this page -->
<!-- inject:css -->
<link rel="stylesheet" href="css/vertical-layout-light/style.css">
<!-- endinject -->
<link rel="shortcut icon" href="images/favicon.png" />
</head>
<body>
<div class="container-scroller">
<!-- partial:partials/_navbar.html -->
<nav class="navbar col-lg-12 col-12 p-0 fixed-top d-flex flex-row">
<div class="text-center navbar-brand-wrapper d-flex align-items-center justify-content-center">
<a class="navbar-brand brand-logo mr-5" href="index.html"><img src="images/logo.svg" class="mr-2"
alt="logo" /></a>
<a class="navbar-brand brand-logo-mini" href="index.html"><img src="images/logo-mini.svg"
alt="logo" /></a>
</div>
<div class="navbar-menu-wrapper d-flex align-items-center justify-content-end">
<button class="navbar-toggler navbar-toggler align-self-center" type="button" data-toggle="minimize">
<span class="icon-menu"></span>
</button>
<ul class="navbar-nav navbar-nav-right">
<li class="nav-item nav-profile dropdown">
<a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown" id="profileDropdown">
<img src="images/faces/face28.jpg" alt="profile" />
</a>
<div class="dropdown-menu dropdown-menu-right navbar-dropdown"
aria-labelledby="profileDropdown">
<a class="dropdown-item" href="/logout">
<i class="ti-power-off text-primary"></i>
Logout
</a>
</div>
</li>
</ul>
</div>
</nav>
<!-- partial -->
<div class="container-fluid page-body-wrapper">
<nav class="sidebar sidebar-offcanvas" id="sidebar">
<ul class="nav">
<li class="nav-item">
<a class="nav-link" href="/">
<i class="icon-grid menu-icon"></i>
<span class="menu-title">Home</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" data-toggle="collapse" href="#manage-links" aria-expanded="false"
aria-controls="manage-links">
<i class="icon-layout menu-icon"></i>
<span class="menu-title">Manage Links</span>
<i class="menu-arrow"></i>
</a>
<div class="collapse" id="manage-links">
<ul class="nav flex-column sub-menu">
<li class="nav-item"> <a class="nav-link" href="/link/add">Add
new</a></li>
<li class="nav-item"> <a class="nav-link" href="/link/add/batch">Batch
upload</a></li>
</ul>
</div>
</li>
<li class="nav-item">
<a class="nav-link" href="/logout">
<i class="icon-gears menu-icon"></i>
<span class="menu-title">Logout</span>
</a>
</li>
</ul>
</nav>
<!-- partial -->
<div class="main-panel">
<div class="content-wrapper">
</div>
<footer class="footer">
<div class="d-sm-flex justify-content-center justify-content-sm-between">
<span class="text-muted text-center text-sm-left d-block d-sm-inline-block">Copyright © 2022.
Premium <a href="https://www.bootstrapdash.com/" target="_blank">Bootstrap admin
template</a> from BootstrapDash. All rights reserved.</span>
<span class="float-none float-sm-right d-block mt-1 mt-sm-0 text-center">Hand-crafted & made
with <i class="ti-heart text-danger ml-1"></i></span>
</div>
</footer>
<!-- partial -->
</div>
<!-- main-panel ends -->
</div>
<!-- page-body-wrapper ends -->
</div>
<!-- container-scroller -->
<!-- plugins:js -->
<script src="vendors/js/vendor.bundle.base.js"></script>
<!-- endinject -->
<!-- Plugin js for this page -->
<script src="vendors/datatables.net/jquery.dataTables.js"></script>
<script src="vendors/datatables.net-bs4/dataTables.bootstrap4.js"></script>
<script src="js/dataTables.select.min.js"></script>
<!-- End plugin js for this page -->
<!-- inject:js -->
<script src="js/off-canvas.js"></script>
<script src="js/hoverable-collapse.js"></script>
<script src="js/template.js"></script>
</body>
</html>Even here, we can see that some section of the pages will always exist in all pages such as the top nav bar and the side bar. So what I usually do is to add this to a separate html file and import it using js instead.
The script to import html from a link can be source from W3
we can add a new includeHtml.js in the js folder
function includeHTML() {
var z, i, elmnt, file, xhttp;
/* Loop through a collection of all HTML elements: */
z = document.getElementsByTagName("*");
for (i = 0; i < z.length; i++) {
elmnt = z[i];
/*search for elements with a certain atrribute:*/
file = elmnt.getAttribute("include-html");
if (file) {
/* Make an HTTP request using the attribute value as the file name: */
xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function () {
if (this.readyState == 4) {
if (this.status == 200) { elmnt.innerHTML = this.responseText; }
if (this.status == 404) { elmnt.innerHTML = "Page not found."; }
/* Remove the attribute, and call this function once more: */
elmnt.removeAttribute("include-html");
includeHTML();
}
}
xhttp.open("GET", file, true);
xhttp.send();
/* Exit the function: */
return;
}
}
}
includeHTML()and we can import the js in the template and add the side bar, top nav, and footer in a separate file leaving us with this final html for our frame:
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>KECIL APP</title>
<!-- plugins:css -->
<link rel="stylesheet" href="vendors/feather/feather.css">
<link rel="stylesheet" href="vendors/ti-icons/css/themify-icons.css">
<link rel="stylesheet" href="vendors/css/vendor.bundle.base.css">
<!-- endinject -->
<!-- Plugin css for this page -->
<link rel="stylesheet" href="vendors/datatables.net-bs4/dataTables.bootstrap4.css">
<link rel="stylesheet" href="vendors/ti-icons/css/themify-icons.css">
<link rel="stylesheet" type="text/css" href="js/select.dataTables.min.css">
<!-- End plugin css for this page -->
<!-- inject:css -->
<link rel="stylesheet" href="css/vertical-layout-light/style.css">
<!-- endinject -->
<link rel="shortcut icon" href="images/favicon.png" />
</head>
<body>
<div class="container-scroller">
<!-- partial:partials/_navbar.html -->
<nav class="navbar col-lg-12 col-12 p-0 fixed-top d-flex flex-row" include-html="/pages/frame/topNav.html">
</nav>
<!-- partial -->
<div class="container-fluid page-body-wrapper">
<nav class="sidebar sidebar-offcanvas" id="sidebar" include-html="/pages/frame/sideBar.html">
</nav>
<!-- partial -->
<div class="main-panel">
<div class="content-wrapper">
</div>
<footer class="footer" include-html="/pages/frame/footer.html">
</footer>
<!-- partial -->
</div>
<!-- main-panel ends -->
</div>
<!-- page-body-wrapper ends -->
</div>
<!-- container-scroller -->
<!-- plugins:js -->
<script src="vendors/js/vendor.bundle.base.js"></script>
<!-- endinject -->
<!-- Plugin js for this page -->
<script src="vendors/datatables.net/jquery.dataTables.js"></script>
<script src="vendors/datatables.net-bs4/dataTables.bootstrap4.js"></script>
<script src="js/dataTables.select.min.js"></script>
<!-- End plugin js for this page -->
<!-- inject:js -->
<script src="js/off-canvas.js"></script>
<script src="js/hoverable-collapse.js"></script>
<script src="js/template.js"></script>
<script src="js/includeHtml.js"></script>
</body>
</html>Create pages
Now that we have cleaned our template, lets start creating the actual pages of our app
Home page
in this page there will be 2 sections, 1 for the user to add a new link, and one for the user to view all of their saved links
HTML Code:
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>KECIL APP</title>
<!-- plugins:css -->
<link rel="stylesheet" href="vendors/feather/feather.css">
<link rel="stylesheet" href="vendors/ti-icons/css/themify-icons.css">
<link rel="stylesheet" href="vendors/css/vendor.bundle.base.css">
<!-- endinject -->
<!-- Plugin css for this page -->
<link rel="stylesheet" href="vendors/datatables.net-bs4/dataTables.bootstrap4.css">
<link rel="stylesheet" href="vendors/ti-icons/css/themify-icons.css">
<link rel="stylesheet" type="text/css" href="js/select.dataTables.min.css">
<!-- End plugin css for this page -->
<!-- inject:css -->
<link rel="stylesheet" href="css/vertical-layout-light/style.css">
<!-- endinject -->
<link rel="shortcut icon" href="images/favicon.png" />
</head>
<body>
<div class="container-scroller">
<!-- partial:partials/_navbar.html -->
<nav class="navbar col-lg-12 col-12 p-0 fixed-top d-flex flex-row" include-html="/pages/frame/topNav.html">
</nav>
<!-- partial -->
<div class="container-fluid page-body-wrapper">
<nav class="sidebar sidebar-offcanvas" id="sidebar" include-html="/pages/frame/sideBar.html">
</nav>
<!-- partial -->
<div class="main-panel">
<div class="content-wrapper">
<div class="row">
<div class="col-12 grid-margin stretch-card">
<div class="card">
<div class="card-body">
<h4 class="card-title">Add new short link</h4>
<p class="card-description">
From Source to Target
</p>
<form class="form-inline">
<div class="input-group col-1">
<button type="button" class="btn btn-warning btn-rounded btn-icon">
<i class="ti-bolt"></i>
</button>
</div>
<div class="input-group col-4">
<div class="input-group-prepend">
<span class="input-group-text">https://link.kecil.com/</span>
</div>
<input type="text" name="source_id" class="form-control"
placeholder="Source ID" aria-label="Source ID">
</div>
<div class="input-group col-5">
<input type="url" name="target_url" class="form-control"
placeholder="Target URL" aria-label="Target URL">
</div>
<div class="input-group col-2">
<button type="submit" class="btn btn-primary mb-2">Shorten it!</button>
</div>
</form>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12 grid-margin stretch-card">
<div class="card">
<div class="card-body">
<h4 class="card-title">All my links</h4>
<p class="card-description">
In here you can view all your created links
</p>
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>ID</th>
<th>Shareable link</th>
<th>Target Link</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
<tr>
<td>2</td>
<td>
<a href="link.kecil.com/yyyyyy">
link.kecil.com/yyyyyy
</a>
</td>
<td>
<a href="https://hi.nicholasbudiharsa.xyz">
https://hi.nicholasbudiharsa.xyz
</a>
</td>
<td>
<label class="badge badge-danger">Delete</label>
</td>
</tr>
<tr>
<td>2</td>
<td>
<a href="link.kecil.com/yyyyyy">
link.kecil.com/yyyyyy
</a>
</td>
<td>
<a href="https://hi.nicholasbudiharsa.xyz">
https://hi.nicholasbudiharsa.xyz
</a>
</td>
<td>
<label class="badge badge-danger">Delete</label>
</td>
</tr>
<tr>
<td>2</td>
<td>
<a href="link.kecil.com/yyyyyy">
link.kecil.com/yyyyyy
</a>
</td>
<td>
<a href="https://hi.nicholasbudiharsa.xyz">
https://hi.nicholasbudiharsa.xyz
</a>
</td>
<td>
<label class="badge badge-danger">Delete</label>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
<footer class="footer" include-html="/pages/frame/footer.html">
</footer>
<!-- partial -->
</div>
<!-- main-panel ends -->
</div>
<!-- page-body-wrapper ends -->
</div>
<!-- container-scroller -->
<!-- plugins:js -->
<script src="vendors/js/vendor.bundle.base.js"></script>
<!-- endinject -->
<!-- Plugin js for this page -->
<script src="vendors/datatables.net/jquery.dataTables.js"></script>
<script src="vendors/datatables.net-bs4/dataTables.bootstrap4.js"></script>
<script src="js/dataTables.select.min.js"></script>
<!-- End plugin js for this page -->
<!-- inject:js -->
<script src="js/off-canvas.js"></script>
<script src="js/hoverable-collapse.js"></script>
<script src="js/template.js"></script>
<script src="js/includeHtml.js"></script>
</body>
</html>Settings
HTML :
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>KECIL APP</title>
<!-- plugins:css -->
<link rel="stylesheet" href="vendors/feather/feather.css">
<link rel="stylesheet" href="vendors/ti-icons/css/themify-icons.css">
<link rel="stylesheet" href="vendors/css/vendor.bundle.base.css">
<!-- endinject -->
<!-- Plugin css for this page -->
<link rel="stylesheet" href="vendors/datatables.net-bs4/dataTables.bootstrap4.css">
<link rel="stylesheet" href="vendors/ti-icons/css/themify-icons.css">
<link rel="stylesheet" type="text/css" href="js/select.dataTables.min.css">
<!-- End plugin css for this page -->
<!-- inject:css -->
<link rel="stylesheet" href="css/vertical-layout-light/style.css">
<!-- endinject -->
<link rel="shortcut icon" href="images/favicon.png" />
</head>
<body>
<div class="container-scroller">
<!-- partial:partials/_navbar.html -->
<nav class="navbar col-lg-12 col-12 p-0 fixed-top d-flex flex-row" include-html="/pages/frame/topNav.html">
</nav>
<!-- partial -->
<div class="container-fluid page-body-wrapper">
<nav class="sidebar sidebar-offcanvas" id="sidebar" include-html="/pages/frame/sideBar.html">
</nav>
<!-- partial -->
<div class="main-panel">
<div class="content-wrapper">
<div class="col-12 grid-margin stretch-card">
<div class="card">
<div class="card-body">
<h4 class="card-title">User Settings</h4>
<form class="forms-sample">
<div class="form-group">
<label for="exampleInputName1">First Name</label>
<input type="text" class="form-control" id="first_name" name="first_name"
placeholder="First Name">
</div>
<div class="form-group">
<label for="exampleInputName1">Last Name</label>
<input type="text" class="form-control" id="last_name" name="last_name"
placeholder="Last Name">
</div>
<div class="form-group">
<label for="exampleInputEmail3">Email address</label>
<input type="email" class="form-control" id="email" name="email"
placeholder="Email">
</div>
<button type="submit" class="btn btn-primary mr-2">Submit</button>
</form>
</div>
</div>
</div>
<div class="col-12 grid-margin stretch-card">
<div class="card">
<div class="card-body">
<h4 class="card-title">Password Settings</h4>
<form class="forms-sample">
<div class="form-group">
<label for="exampleInputName1">Current Password</label>
<input type="password" class="form-control" id="cur_pw" name="cur_pw"
placeholder="Current Password">
</div>
<div class="form-group">
<label for="exampleInputName1">New Password</label>
<input type="password" class="form-control" id="new_pw" name="new_pw"
placeholder="New Password">
</div>
<button type="submit" class="btn btn-primary mr-2">Save</button>
</form>
</div>
</div>
</div>
</div>
<footer class="footer" include-html="/pages/frame/footer.html">
</footer>
<!-- partial -->
</div>
<!-- main-panel ends -->
</div>
<!-- page-body-wrapper ends -->
</div>
<!-- container-scroller -->
<!-- plugins:js -->
<script src="vendors/js/vendor.bundle.base.js"></script>
<!-- endinject -->
<!-- Plugin js for this page -->
<script src="vendors/datatables.net/jquery.dataTables.js"></script>
<script src="vendors/datatables.net-bs4/dataTables.bootstrap4.js"></script>
<script src="js/dataTables.select.min.js"></script>
<!-- End plugin js for this page -->
<!-- inject:js -->
<script src="js/off-canvas.js"></script>
<script src="js/hoverable-collapse.js"></script>
<script src="js/template.js"></script>
<script src="js/includeHtml.js"></script>
</body>
</html>Authentication forms (Login, Register, Reset password, Forgot password)
HTML for login page:
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Skydash Admin</title>
<!-- plugins:css -->
<link rel="stylesheet" href="/vendors/feather/feather.css">
<link rel="stylesheet" href="/vendors/ti-icons/css/themify-icons.css">
<link rel="stylesheet" href="/vendors/css/vendor.bundle.base.css">
<!-- endinject -->
<!-- Plugin css for this page -->
<!-- End plugin css for this page -->
<!-- inject:css -->
<link rel="stylesheet" href="/css/vertical-layout-light/style.css">
<!-- endinject -->
<link rel="shortcut icon" href="/images/favicon.png" />
</head>
<body>
<div class="container-scroller">
<div class="container-fluid page-body-wrapper full-page-wrapper">
<div class="content-wrapper d-flex align-items-center auth px-0">
<div class="row w-100 mx-0">
<div class="col-lg-4 mx-auto">
<div class="auth-form-light text-left py-5 px-4 px-sm-5">
<div class="brand-logo">
<img src="/images/logo.svg" alt="logo">
</div>
<h4>Hello! welcome to Kecil App</h4>
<h6 class="font-weight-light">Sign in to continue.</h6>
<form class="pt-3">
<div class="form-group">
<input type="email" class="form-control form-control-lg" id="email"
placeholder="Email">
</div>
<div class="form-group">
<input type="password" class="form-control form-control-lg" name="password"
placeholder="Password">
</div>
<div class="mt-3">
<a class="btn btn-block btn-primary btn-lg font-weight-medium auth-form-btn"
href="/">SIGN IN</a>
</div>
<div class="text-center mt-4 font-weight-light">
Don't have an account? <a href="/register" class="text-primary">Create</a>
</div>
<div class="text-center mt-4 font-weight-light">
<a href="/forgot-password" class="auth-link text-black">Forgot password?</a>
</div>
</form>
</div>
</div>
</div>
</div>
<!-- content-wrapper ends -->
</div>
<!-- page-body-wrapper ends -->
</div>
<!-- container-scroller -->
<!-- plugins:js -->
<script src="/vendors/js/vendor.bundle.base.js"></script>
<!-- endinject -->
<!-- Plugin js for this page -->
<!-- End plugin js for this page -->
<!-- inject:js -->
<script src="/js/off-canvas.js"></script>
<script src="/js/hoverable-collapse.js"></script>
<script src="/js/template.js"></script>
<script src="/js/settings.js"></script>
<script src="/js/todolist.js"></script>
<!-- endinject -->
</body>
</html>HTML for register page:
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Skydash Admin</title>
<!-- plugins:css -->
<link rel="stylesheet" href="/vendors/feather/feather.css">
<link rel="stylesheet" href="/vendors/ti-icons/css/themify-icons.css">
<link rel="stylesheet" href="/vendors/css/vendor.bundle.base.css">
<!-- endinject -->
<!-- Plugin css for this page -->
<!-- End plugin css for this page -->
<!-- inject:css -->
<link rel="stylesheet" href="/css/vertical-layout-light/style.css">
<!-- endinject -->
<link rel="shortcut icon" href="/images/favicon.png" />
</head>
<body>
<div class="container-scroller">
<div class="container-fluid page-body-wrapper full-page-wrapper">
<div class="content-wrapper d-flex align-items-center auth px-0">
<div class="row w-100 mx-0">
<div class="col-lg-4 mx-auto">
<div class="auth-form-light text-left py-5 px-4 px-sm-5">
<div class="brand-logo">
<img src="/images/logo.svg" alt="logo">
</div>
<h4>New here?</h4>
<h6 class="font-weight-light">Signing up is easy. It only takes a few steps</h6>
<form class="pt-3">
<div class="form-group">
<input type="text" name="first_name" class="form-control form-control-lg" placeholder="First Name">
</div>
<div class="form-group">
<input type="text" name="last_name" class="form-control form-control-lg" placeholder="Last Name">
</div>
<div class="form-group">
<input type="email" class="form-control form-control-lg" id="exampleInputEmail1" placeholder="Email">
</div>
<div class="form-group">
<input type="password" class="form-control form-control-lg" id="exampleInputPassword1"
placeholder="Password">
</div>
<div class="mb-4">
<div class="form-check">
<label class="form-check-label text-muted">
<input type="checkbox" class="form-check-input" required>
I agree to all Terms & Conditions
</label>
</div>
</div>
<div class="mt-3">
<a class="btn btn-block btn-primary btn-lg font-weight-medium auth-form-btn" href="/login">SIGN
UP</a>
</div>
<div class="text-center mt-4 font-weight-light">
Already have an account? <a href="/login" class="text-primary">Login</a>
</div>
</form>
</div>
</div>
</div>
</div>
<!-- content-wrapper ends -->
</div>
<!-- page-body-wrapper ends -->
</div>
<!-- container-scroller -->
<!-- plugins:js -->
<script src="/vendors/js/vendor.bundle.base.js"></script>
<!-- endinject -->
<!-- Plugin js for this page -->
<!-- End plugin js for this page -->
<!-- inject:js -->
<script src="/js/off-canvas.js"></script>
<script src="/js/hoverable-collapse.js"></script>
<script src="/js/template.js"></script>
<script src="/js/settings.js"></script>
<script src="/js/todolist.js"></script>
<!-- endinject -->
</body>
</html>HTML for forgot your password:
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Skydash Admin</title>
<!-- plugins:css -->
<link rel="stylesheet" href="/vendors/feather/feather.css">
<link rel="stylesheet" href="/vendors/ti-icons/css/themify-icons.css">
<link rel="stylesheet" href="/vendors/css/vendor.bundle.base.css">
<!-- endinject -->
<!-- Plugin css for this page -->
<!-- End plugin css for this page -->
<!-- inject:css -->
<link rel="stylesheet" href="/css/vertical-layout-light/style.css">
<!-- endinject -->
<link rel="shortcut icon" href="/images/favicon.png" />
</head>
<body>
<div class="container-scroller">
<div class="container-fluid page-body-wrapper full-page-wrapper">
<div class="content-wrapper d-flex align-items-center auth px-0">
<div class="row w-100 mx-0">
<div class="col-lg-4 mx-auto">
<div class="auth-form-light text-left py-5 px-4 px-sm-5">
<div class="brand-logo">
<img src="/images/logo.svg" alt="logo">
</div>
<h4>Forgot password ?</h4>
<h6 class="font-weight-light">Resetting it is easy</h6>
<form class="pt-3">
<div class="form-group">
<input type="email" class="form-control form-control-lg" id="email"
placeholder="Email">
</div>
<div class="mt-3">
<a class="btn btn-block btn-primary btn-lg font-weight-medium auth-form-btn"
href="/login">SEND RESET PASSWORD LINK</a>
</div>
<div class="text-center mt-4 font-weight-light">
Return to <a href="/login" class="auth-link text-black">Login</a> page
</div>
</form>
</div>
</div>
</div>
</div>
<!-- content-wrapper ends -->
</div>
<!-- page-body-wrapper ends -->
</div>
<!-- container-scroller -->
<!-- plugins:js -->
<script src="/vendors/js/vendor.bundle.base.js"></script>
<!-- endinject -->
<!-- Plugin js for this page -->
<!-- End plugin js for this page -->
<!-- inject:js -->
<script src="/js/off-canvas.js"></script>
<script src="/js/hoverable-collapse.js"></script>
<script src="/js/template.js"></script>
<script src="/js/settings.js"></script>
<script src="/js/todolist.js"></script>
<!-- endinject -->
</body>
</html>Error page
HTML Code
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Skydash Admin</title>
<!-- plugins:css -->
<link rel="stylesheet" href="/vendors/feather/feather.css">
<link rel="stylesheet" href="/vendors/ti-icons/css/themify-icons.css">
<link rel="stylesheet" href="/vendors/css/vendor.bundle.base.css">
<!-- endinject -->
<!-- Plugin css for this page -->
<!-- End plugin css for this page -->
<!-- inject:css -->
<link rel="stylesheet" href="/css/vertical-layout-light/style.css">
<!-- endinject -->
<link rel="shortcut icon" href="/images/favicon.png" />
</head>
<body>
<div class="container-scroller">
<div class="container-fluid page-body-wrapper full-page-wrapper">
<div class="content-wrapper d-flex align-items-center text-center error-page bg-info">
<div class="row flex-grow">
<div class="col-lg-7 mx-auto text-white">
<div class="row align-items-center d-flex flex-row">
<div class="col-lg-6 text-lg-right pr-lg-4">
<h1 class="display-1 mb-0">500</h1>
</div>
<div class="col-lg-6 error-page-divider text-lg-left pl-lg-4">
<h2>SORRY!</h2>
<h3 class="font-weight-light">Internal server error!</h3>
</div>
</div>
<div class="row mt-5">
<div class="col-12 text-center mt-xl-2">
<a class="text-white font-weight-medium" href="/index.html">Back to home</a>
</div>
</div>
<div class="row mt-5">
<div class="col-12 mt-xl-2">
<p class="text-white font-weight-medium text-center">Copyright © 2021 All rights reserved.</p>
</div>
</div>
</div>
</div>
</div>
<!-- content-wrapper ends -->
</div>
<!-- page-body-wrapper ends -->
</div>
<!-- container-scroller -->
<!-- plugins:js -->
<script src="/vendors/js/vendor.bundle.base.js"></script>
<!-- endinject -->
<!-- Plugin js for this page -->
<!-- End plugin js for this page -->
<!-- inject:js -->
<script src="/js/off-canvas.js"></script>
<script src="/js/hoverable-collapse.js"></script>
<script src="/js/template.js"></script>
<script src="/js/settings.js"></script>
<script src="/js/todolist.js"></script>
<!-- endinject -->
</body>
</html>Make a server that serves the html
To finalize this part, we will create a server in our app.js with an express server by adding the following lines in app.js:
var express = require('express');
var bodyParser = require('body-parser');
var app = express()
app.use('/pages', express.static(__dirname + '/public/pages'));
app.use('/css', express.static(__dirname + '/public/css'));
app.use('/fonts', express.static(__dirname + '/public/fonts'));
app.use('/js', express.static(__dirname + '/public/js'));
app.use('/partials', express.static(__dirname + '/public/partials'));
app.use('/scss', express.static(__dirname + '/public/scss'));
app.use('/vendors', express.static(__dirname + '/public/vendors'));
app.use('/images', express.static(__dirname + '/public/images'));
// FOR LOGGING
app.use((req, res, next) => {
console.log(`[${(new Date()).toISOString()}] ${req.path} - IP : ${req.headers['x-forwarded-for'] || req.socket.remoteAddress}`)
next()
})
const urlencodedParser = bodyParser.urlencoded({ extended: true })
app.engine('html', require('ejs').renderFile);
app.use(urlencodedParser)
app.use(express.urlencoded({ extended: false }))
app.get('/', function (req, res) {
res.render(__dirname + '/public/pages/home.html')
})
app.get('/login', function (req, res) {
res.render(__dirname + '/public/pages/auth/login.html')
})
app.get('/forgot-password', function (req, res) {
res.render(__dirname + '/public/pages/auth/forgot-password.html')
})
app.get('/register', function (req, res) {
res.render(__dirname + '/public/pages/auth/register.html')
})
app.get('/setting', function (req, res) {
res.render(__dirname + '/public/pages/setting.html')
})
app.get('/error', function (req, res) {
res.render(__dirname + '/public/pages/error.html')
})
app.get('/logout', function (req, res) {
res.redirect("/login")
})
const server = app.listen(7000, () => {
console.log('Express listening at ', 7000);
})Now if you run node app.js in the terminal, you can now access your app in the https://127.0.0.1:7000/ !
Conclusion
By the end of this part, we have finalized the design of our link shortener app, and its starting to look good!
Do not hesitate to look at the code for this project which is hosted in Github
In the next part, we will talk about adding our API Backend to the server, so lets get started 😊