Hypothesis
There is a paid parking lot, with the following rates: $1 for the first hour and $0.5 for every subsequent hour. The parking capacity is of 10 spaces.
Required
Make an activity management system for the Parking with the following characteristics:
- Any parking interval is rounded up, to the nearest hour.
- When a new car enters the parking lot, the parking system takes in the registration number of the car.
- When a car leaves the parking, the system issues a summary, depending on the duration of the stay. What information do you consider essential as part of the summary?
- Any customer can see the list of cars parked in the parking lot at a given time.
Technical requirements
- The implementation will be done using JavaScript, all data being stored in memory, with no need for permanent storage.
- Write clean code.
- Create a simple GUI that allows entering and reading data from the computer; display the data on the same page.
Solution
I used Bootstrap 4 for aesthetics. The script itself uses only JavaScript. jQuery is included for the corect functioning of Bootstrap.
let cars = [];
const addCarButton = document.querySelector('#carButton');
const minLicenseeLength = 7;
const payPerHour = 0.5;
const payFirstHour = 1;
const totalPlaces = 10;
const formatDate = (date) => {
var hours = date.getHours();
var minutes = date.getMinutes();
var ampm = hours >= 12 ? 'PM' : 'AM';
hours = hours % 12;
hours = hours ? hours : 12;
minutes = minutes < 10 ? '0' + minutes : minutes;
var strTime = hours + ':' + minutes + ' ' + ampm;
return date.getDate() + "/" + (date.getMonth() + 1) + "/" + date.getFullYear() + " " + strTime;
}
const secondsToHours = (d) => {
d = Number(d);
let h = Math.ceil(d / 3600);
return h;
}
const renterTable = () => {
let results = '';
for (var i = 0; i < cars.length; i++) {
let licensee = cars[i].licensee;
let arrival = formatDate(cars[i].arrival);
let leave = cars[i].leave === '-' ? '-' : formatDate(cars[i].leave);
results += `<tr>
<td>${licensee}</td>
<td>${arrival}</td>
<td>${leave}</td>
<td>${showStatus(cars[i])}</td>
<td class="text-right">${makeBill(cars[i])}</td>
<td class="text-right">
<button data-row="${i}" onclick="showSummary(event)" data-toggle="modal" data-target="#myModal" class="btn btn-sm btn-success">Summary</button>
</td>
</tr>`;
}
document.querySelector("#parking tbody").innerHTML = results;
}
const showStatus = (car) => {
return car.isParked ? "Parked" : "Has left";
}
const changeStatus = (event) => {
cars[event.target.dataset.row].isParked = false;
}
const setLeaveTime = (event) => {
cars[event.target.dataset.row].leave = new Date(Date.now());
}
const countAvailablePlaces = (event) => {
document.querySelector('#placesCount').innerHTML = totalPlaces - cars.length;
}
const setClassForBadge = () => {
let badgeClassName = cars.length == totalPlaces ? 'badge badge-danger' : 'badge badge-success';
document.querySelector('#placesCount').setAttribute('class', badgeClassName);
}
const calculateHoursBilled = (car) => {
let arrivedAt = new Date(car.arrival).getTime();
let leftAt = new Date(car.leave).getTime();
return secondsToHours((leftAt - arrivedAt) / 1000); //duration in seconds
}
const makeBill = (car) => {
let hoursBilled = calculateHoursBilled(car);
let billValue = car.isParked ? "-" : "$" + (payFirstHour + (hoursBilled - 1) * payPerHour);
return billValue;
}
const printSummary = (event) => {
let car = cars[event.target.dataset.row];
let sumarryTable = `<table class="table table-bordered m-0">
<tr>
<td class="font-weight-bold">Registration number</td>
<td>${car.licensee}</td>
</tr>
<tr>
<td class="font-weight-bold">Arrival</td>
<td>${formatDate(car.arrival)}</td>
</tr>
<tr>
<td class="font-weight-bold">Departure</td>
<td>${formatDate(car.leave)}</td>
</tr>
<tr>
<td class="font-weight-bold">Billable hours</td>
<td>${calculateHoursBilled(car)}</td>
</tr>
<tr>
<td class="font-weight-bold">Bill value</td>
<td>${makeBill(car)}</td>
</tr></table>`;
document.querySelector('#modalBody').innerHTML = sumarryTable;
}
const showSummary = (event) => {
changeStatus(event);
setLeaveTime(event);
renterTable();
printSummary(event);
//Free the parking place, 3 seconds after the summary is released
setTimeout(function() {
freeSpot(event);
}, 3000);
}
const addCar = () => {
let newLicensee = document.querySelector("#carValue").value;
let newCar = {
licensee: newLicensee,
arrival: new Date(),
leave: '-',
isParked: true
}
// Add new car to the cars array
document.querySelector('#message').style.display = 'none';
if (newLicensee.length >= minLicenseeLength && cars.length < totalPlaces) {
cars.unshift(newCar);
} else {
if (newLicensee.length < minLicenseeLength) {
document.querySelector('#message').style.display = 'block';
}
}
if (cars.length == totalPlaces) {
document.querySelector('#carButton').setAttribute('disabled', true);
}
setClassForBadge();
//Update places count
countAvailablePlaces(event);
// Empty text box
document.querySelector("#carValue").value = '';
// Render the table
renterTable();
}
const freeSpot = (event) => {
cars.splice(event.target.dataset.row, 1);
setClassForBadge();
if (cars.length == totalPlaces) {
document.querySelector('#carButton').setAttribute('disabled');
} else {
document.querySelector('#carButton').removeAttribute('disabled');
}
// Render Table again after delete
renterTable();
//Update places count
countAvailablePlaces(event);
}
// Add new car to the array
addCarButton.addEventListener('click', addCar);
// Render Table
renterTable();
//Show places count at page load
countAvailablePlaces(event);
#addForm {
position: relative;
}
#message {
display: none;
position: relative;
font-size: 10px;
position: absolute;
}
#placesCount {
font-size: 90%;
}
#parking th,
#parking td {
white-space: nowrap;
font-size: 14px;
}
#myModal .font-weight-bold {
font-weight: 500 !important;
}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
<div class="container">
<div class="card my-3">
<div class="card-header px-3 d-flex">
<h5 class="m-0">Parking management</h5>
<div class="ml-auto">
<span id="placesCount" class="badge badge-success"></span> available places
</div>
</div>
<div class="card-body p-0">
<div class="input-group p-2" id="addForm">
<input type="text" class="form-control" id="carValue" placeholder="Registration number">
<div class="input-group-append">
<button id="carButton" class="btn btn-sm btn-success">Park car</button>
</div>
<p id="message" class="text-danger m-0">Registration number invalid</p>
</div>
<div class="table-responsive">
<table id="parking" class="table table-striped m-0">
<thead>
<tr>
<th>Registration no</th>
<th>Arrival</th>
<th>Departure</th>
<th>Status</th>
<th class="text-right">Bill</th>
<th class="text-right">Actions</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
</div>
<div class="modal" id="myModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h6 class="modal-title">Sumar Parcare</h6>
<button type="button" class="close" data-dismiss="modal">×</button>
</div>
<div id="modalBody" class="modal-body"></div>
</div>
</div>
</div>
Do you see any problems with this application, including from a security stand-point?
Thanks!
countAvailablePlaces
is doing two things: the maths and the rendering of the result. These concerns could be separated. \$\endgroup\$ – 52d6c6af Oct 20 '20 at 14:31