Inital Commit

This commit is contained in:
2023-01-28 22:01:59 +01:00
commit 116dc1dcb0
16 changed files with 1980 additions and 0 deletions

64
.gitignore vendored Normal file
View File

@@ -0,0 +1,64 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Typescript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# next.js build output
.next
#Database Config
database.js

58
BikeRides.sql Normal file
View File

@@ -0,0 +1,58 @@
-- SQL Dump
-- Server version: 8.0.31
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
START TRANSACTION;
SET time_zone = "+00:00";
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
--
-- Database: `BikeTesting`
--
-- --------------------------------------------------------
--
-- Table structure for table `BikeRides`
--
CREATE TABLE `BikeRides` (
`id` int NOT NULL,
`start_point` varchar(30) NOT NULL,
`end_point` varchar(30) NOT NULL,
`route_id` int NOT NULL,
`time_taken` int NOT NULL,
`average_speed` int NOT NULL,
`departure` datetime NOT NULL,
`arival` datetime NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
--
-- Indexes for dumped tables
--
--
-- Indexes for table `BikeRides`
--
ALTER TABLE `BikeRides`
ADD PRIMARY KEY (`id`);
--
-- AUTO_INCREMENT for dumped tables
--
--
-- AUTO_INCREMENT for table `BikeRides`
--
ALTER TABLE `BikeRides`
MODIFY `id` int NOT NULL AUTO_INCREMENT;
COMMIT;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

8
Readme.md Normal file
View File

@@ -0,0 +1,8 @@
# BikeRideViewer
Install using `npm install`
Dev-Enviroment: NodeJS 18 and MySQL
Build with Express.JS framework
Based on this [Soure](https://www.webslesson.info/2022/04/insert-update-delete-data-from-mysql-in-node-js-using-express-js.html#step3)

64
app.js Normal file
View File

@@ -0,0 +1,64 @@
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var session = require('express-session');
var flash = require('connect-flash');
var indexRouter = require('./routes/index');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
// Load MDI
app.use("/css/materialdesignicons.min.css", express.static(path.join(__dirname, "node_modules/@mdi/font/css/materialdesignicons.min.css")));
app.use("/css/materialdesignicons.css.map", express.static(path.join(__dirname, "node_modules/@mdi/font/css/materialdesignicons.css.map")));
app.use("/fonts/materialdesignicons-webfont.eot", express.static(path.join(__dirname, "node_modules/@mdi/font/fonts/materialdesignicons-webfont.eot")));
app.use("/fonts/materialdesignicons-webfont.ttf", express.static(path.join(__dirname, "node_modules/@mdi/font/fonts/materialdesignicons-webfont.ttf")));
app.use("/fonts/materialdesignicons-webfont.woff", express.static(path.join(__dirname, "node_modules/@mdi/font/fonts/materialdesignicons-webfont.woff")));
app.use("/fonts/materialdesignicons-webfont.woff2", express.static(path.join(__dirname, "node_modules/@mdi/font/fonts/materialdesignicons-webfont.woff2")));
//Load table-sort-js
app.use("/js/table-sort.js", express.static(path.join(__dirname, "node_modules/table-sort-js/table-sort.js")));
app.use(session({
secret : 'ANL8cIXDN2f4nHDJSdoUAegACr7KfrYR',
cookie : {maxAge : 60000},
saveUninitialized : false,
resave : false
}));
app.use(flash());
app.use('/', indexRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;

90
bin/www Executable file
View File

@@ -0,0 +1,90 @@
#!/usr/bin/env node
/**
* Module dependencies.
*/
var app = require('../app');
var debug = require('debug')('biketable:server');
var http = require('http');
/**
* Get port from environment and store in Express.
*/
var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);
/**
* Create HTTP server.
*/
var server = http.createServer(app);
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
/**
* Normalize a port into a number, string, or false.
*/
function normalizePort(val) {
var port = parseInt(val, 10);
if (isNaN(port)) {
// named pipe
return val;
}
if (port >= 0) {
// port number
return port;
}
return false;
}
/**
* Event listener for HTTP server "error" event.
*/
function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}
var bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}
/**
* Event listener for HTTP server "listening" event.
*/
function onListening() {
var addr = server.address();
var bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
debug('Listening on ' + bind);
}

21
database.js.examle Normal file
View File

@@ -0,0 +1,21 @@
const mysql = require('mysql');
var connection = mysql.createConnection({
host : 'localhost',
database : 'testing',
user : 'root',
password : ''
});
connection.connect(function(error){
if(error)
{
throw error;
}
else
{
console.log('MySQL Database is connected Successfully');
}
});
module.exports = connection;

1225
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

21
package.json Normal file
View File

@@ -0,0 +1,21 @@
{
"name": "biketable",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "node ./bin/www"
},
"dependencies": {
"@mdi/font": "^7.1.96",
"connect-flash": "^0.1.1",
"cookie-parser": "~1.4.4",
"debug": "~2.6.9",
"ejs": "~2.6.1",
"express": "~4.16.1",
"express-session": "^1.17.3",
"http-errors": "~1.6.3",
"morgan": "~1.9.1",
"mysql": "^2.18.1",
"table-sort-js": "^1.8.1"
}
}

60
public/css/button.css Normal file
View File

@@ -0,0 +1,60 @@
/*Genral Button Style*/
.btn {
display: inline-block;
align-items: center;
background: none;
border: 1px solid #bdbdbd;
height: 48px;
letter-spacing: 0.25px;
border-radius: 24px;
cursor: pointer;
}
.btn:focus {
outline: none;
}
.btn .mdi {
margin-right: 5px;
margin-left: 5px;
}
/*Delete Button Style*/
.btn-delete {
font-size: 16px;
color: red;
}
.btn-delete:hover, .btn-delete:focus {
background-color: var(--btn-hover-bg-color);
}
.btn-delete > .mdi-delete-empty {
display: none;
}
.btn-delete:hover > .mdi-delete-empty {
display: inline-block;
}
.btn-delete:hover > .mdi-delete {
display: none;
}
/*Info Button Style*/
.btn-info {
font-size: 16px;
color: skyblue;
}
.btn-info > .mdi-information {
display: none;
}
.btn-info:hover > .mdi-information {
display: inline-block;
}
.btn-info:hover > .mdi-information-outline {
display: none;
}

70
public/css/modal.css Normal file
View File

@@ -0,0 +1,70 @@
/*Modal Style*/
.modal {
margin: 100px auto;
padding: 20px;
background: var(--bg-color);
border: 1px solid #666;
width: 300px;
border-radius: 6px;
box-shadow: 0 0 50px rgba(0, 0, 0, 0.5);
position: relative;
}
.modal h2 {
margin-top: 0;
}
.modal .close {
position: absolute;
width: 20px;
height: 20px;
top: 20px;
right: 20px;
opacity: 0.8;
transition: all 200ms;
font-size: 24px;
font-weight: bold;
text-decoration: none;
color: var(--txt-color);
}
.modal .close:hover {
opacity: 1;
}
.modal .content {
max-height: 400px;
overflow: auto;
}
.modal p {
margin: 0 0 1em;
text-align: center;
}
.modal p:last-child {
margin: 0;
}
/*Modal background Style*/
.overlay {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background: rgba(0, 0, 0, 0.5);
transition: opacity 200ms;
opacity: 1;
}
.overlay .cancel {
position: absolute;
width: 100%;
height: 100%;
cursor: default;
}
.overlay:target {
visibility: visible;
opacity: 1;
}

78
public/css/style.css Normal file
View File

@@ -0,0 +1,78 @@
/*Ligt theme colors*/
:root {
--bg-color: #f8f8f8;
--txt-color: #212121;
--lnk-color: #0033cc;
--table-border-color:#eeeeee ;
--table-bg-color: #ffffff;
--table-bg-hover-color:#f3f3f3;
--btn-hover-bg-color: #d2d2d2;
}
/* Dark theme colors*/
@media (prefers-color-scheme: dark) {
:root {
--bg-color: #383c4a;
--txt-color: #bbc0ca;
--lnk-color: #809fff;
--table-border-color: #373c48 ;
--table-bg-color:#2a2e3a ;
--table-bg-hover-color: #373b46;
--btn-hover-bg-color: #2a2e3a;
--color-footer: #2e323e;
}
}
/*Genral Style*/
body {
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
color: var(--txt-color);
background: var(--bg-color);
font: 100% system-ui;
}
.container{
padding: 1%;
}
a {
color: var(--lnk-color);
}
.HeadIcon {
color: #0046a1;
font-size: 100px;
}
.text-center {
text-align: center;
}
.alert.alert-danger {
margin-bottom: 5px;
text-align: center;
border-radius: 50px;
border: 2px darkred solid;
background: red;
max-width: 250px;
}
footer {
position: fixed;
left: 0;
bottom: 0;
width: 100%;
text-align: center;
padding: 5px 0;
background-color: var(--color-footer);
color: var(--txt-color);
}
footer >.right {
float: right;
padding-right: 1%;
}
footer > .left {
float: left;
padding-left: 1%;
}

41
public/css/table.css Normal file
View File

@@ -0,0 +1,41 @@
tbody > tr:hover {
background-color: var(--table-bg-hover-color);
}
table {
border: 1px solid var(--table-border-color);
margin: 0;
padding: 0;
width: 100%;
border-collapse: collapse;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
overflow: hidden;
}
table caption {
font-size: 1.5em;
margin: .5em 0 .75em;
}
table tbody tr {
background-color: var(--table-bg-color);
border: 1px solid var(--table-border-color);
padding: .35em;
}
table thead tr {
background-color: #404652;
border: 1px solid var(--table-border-color);
padding: .35em;
}
table th, table td {
padding: .625em;
text-align: center;
}
table th {
font-size: .85em;
letter-spacing: .1em;
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

64
routes/index.js Normal file
View File

@@ -0,0 +1,64 @@
var express = require('express');
var router = express.Router();
var database = require('../database');
router.get("/", function(request, response, next){
var query = "SELECT * FROM BikeRides ORDER BY id DESC";
database.query(query, function(error, data){
if(error)
{
throw error;
}
else
{
response.render('index', {title:'BikeRides', action:'list', TheData:data, message:request.flash('error')});
}
});
});
router.get('/view/:id', function(request, response, next){
var id = request.params.id;
var query = `SELECT * FROM BikeRides WHERE id = "${id}"`;
database.query(query, function(error, data){
response.render('index', {title: 'View ride', action:'view', data:data[0]});
});
});
router.get('/delete/:id', function(request, response, next){
var id = request.params.id;
var query = `
DELETE FROM BikeRides WHERE id = "${id}"
`;
database.query(query, function(error, data){
if(error)
{
throw error;
}
else
{
request.flash('error', 'Ride removed');
response.redirect("/");
}
});
});
module.exports = router;

3
views/error.ejs Normal file
View File

@@ -0,0 +1,3 @@
<h1><%= message %></h1>
<h2><%= error.status %></h2>
<pre><%= error.stack %></pre>

113
views/index.ejs Normal file
View File

@@ -0,0 +1,113 @@
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- CSS -->
<link rel='stylesheet' href='/css/materialdesignicons.min.css' />
<link rel='stylesheet' href='/css/style.css' />
<!-- JS Scripts -->
<script src="/js/table-sort.js"></script>
<title><%= title %></title>
</head>
<body>
<% if(action == 'view') { %>
<!-- Ride Page -->
<link rel='stylesheet' href='/css/modal.css' />
<link rel='stylesheet' href='/css/button.css' />
<div id="modal" class="overlay">
<a class="cancel" href="#" onclick="history.go(-1)"></a>
<div class="modal">
<h2>Ride info</h2>
<div class="content">
<% if( data !== undefined ) {%>
<p>Date: <%= data.date %></p>
<p>From: <%= data.start_point %></p>
<p>To: <%= data.end_point %></p>
<p>RoutID <%= data.route_id %></p>
<p>Time Taken: <%= data.time_taken %> Min</p>
<p>Average speed: <%= data.average_speed %> Km/h</p>
<p>Departed : <%= new Date (data.departure).toLocaleTimeString("en-US" , { hour: 'numeric', minute: 'numeric'}); %> <br> <%= new Date (data.departure).toLocaleDateString("en-GB", { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric'}) %> </p>
<p>Arived : <%= new Date (data.arival).toLocaleTimeString("en-US" , { hour: 'numeric', minute: 'numeric'}); %> <br> <%= new Date (data.arival).toLocaleDateString("en-GB", { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric'}) %></p>
<button class="btn btn-delete" onclick="location.href='/delete/<%= data.id %>'">
<span class="mdi mdi-delete mdi-24px"></span>
<span class="mdi mdi-delete-empty mdi-24px"></span>
</button>
<% } else { %>
<p style="color: red;"> Error: This ride does not exist</p>
<% } %>
</div>
</div>
</div>
<% } else { %>
<!-- Overview Page -->
<link rel='stylesheet' href='/css/table.css' />
<div class="container">
<h1 class="text-center mt-3 mb-3">
<span class="mdi mdi-bicycle HeadIcon"></span><br>
<%= title %>
</h1>
<% if(message.length > 0) { %>
<div class="alert alert-danger"><%= message %></div>
<% } %>
<div style="overflow-x:auto;">
<table class="table table-sort table-arrows">
<thead>
<tr>
<th>Date</th>
<th>From</th>
<th>To</th>
<th>Time taken</th>
<th>Avg. Speed</th>
<!-- Uncomment next line to add an action row (Delete and Info button) to the table
<th class="TableActions">Action</th>
-->
</tr>
</thead>
<tbody>
<!-- Check if there Rides loaded from DB -->
<%
if(TheData.length > 0)
<!-- If there are Rides output as table -->
{ TheData.forEach(function(data){
%>
<tr onclick="location.href='/view/<%= data.id %>'">
<td><%= new Date (data.departure).toLocaleDateString("en-GB", { weekday: 'short', year: 'numeric', month: 'short', day: 'numeric'}) %></td>
<td><%= data.start_point %></td>
<td><%= data.end_point %></td>
<td><%= data.time_taken %> Min</td>
<td><%= data.average_speed %> Km/h</td>
<!-- Uncomment next lines to add an action row (Delete and Info button) to the table
<td class="TableActions">
<button class="btn btn-delete" onclick="location.href='/delete/<%= data.id %>'">
<span class="mdi mdi-delete mdi-24px"></span>
<span class="mdi mdi-delete-empty mdi-24px"></span>
</button>
<button class="btn btn-info" onclick="location.href='/view/<%= data.id %>'">
<span class="mdi mdi-information-outline mdi-24px"></span>
<span class="mdi mdi-information mdi-24px"></span>
</button>
</td>
-->
</tr>
<% }); } else { %>
<tr>
<td colspan="5">No Data Found</td>
</tr>
<% } %>
</tbody>
</table>
</div>
</div>
<% } %>
<footer>
<div class="left">
<p>BikeRideViewer | By brammp</p>
</div>
<div class="right">
<p><a href="https://git.bprieshof.nl/brammp/BikeRideViewer"><span class="mdi mdi-git mdi-18px"></span>SourceCode</a></p>
</div>
</footer>
</body>
</html>