first commit

This commit is contained in:
MatMasIt 2021-11-01 22:58:00 +01:00
commit 9a5eae5ce1
15 changed files with 710 additions and 0 deletions

1
README.md Normal file
View File

@ -0,0 +1 @@
# adamoCellar

103
api.php Normal file
View File

@ -0,0 +1,103 @@
<?php
header("Content-Type: application/json");
$pdo = new PDO("sqlite:database.db");
$columns = ["vino", "cantina", "annata", "volume", "quantita", "note"];
//header("Content-Type: application/json");
switch ($_GET["intent"]) {
case "list":
$_GET["pageSize"] = (int) $_GET["pageSize"];
$_GET["pageNum"] = (int) $_GET["pageNum"];
$data = [];
$addition = "";
foreach ($columns as $e) {
if (!empty($_GET[$e])) {
$addition .= " $e LIKE ('%' || :$e || '%') AND ";
$data[":" . $e] = $_GET[$e];
}
}
$addition .= " 1=1 ";
$sql = "SELECT * FROM Bottiglie WHERE $addition ";
if (!in_array($_GET["orderBy"], $columns)) $by = "id";
else $by = $_GET["orderBy"];
if (in_array($_GET["orderHow"], ["ASC", "DESC"])) $how = $_GET["orderHow"];
else $how = "ASC";
$sql .= " ORDER BY $by $how";
$sql .= " LIMIT " . $_GET["pageSize"] . " OFFSET " . ($_GET["pageSize"] * $_GET["pageNum"]);
$p = $pdo->prepare($sql);
$p->execute($data);
$res = [];
$res["rows"] = $p->fetchAll(PDO::FETCH_ASSOC);
$sql = "SELECT COUNT(*) AS c FROM Bottiglie WHERE $addition";
$p = $pdo->prepare($sql);
$p->execute($data);
$res["pagTot"] = (int)($p->fetch(PDO::FETCH_ASSOC)["c"] / $_GET["pageSize"]) + 1;
echo json_encode($res);
break;
case "get":
$p = $pdo->prepare("SELECT * FROM Bottiglie WHERE id=:id");
$p->execute([":id" => $_GET["id"]]);
echo json_encode($p->fetch(PDO::FETCH_ASSOC));
break;
case "delete":
$p = $pdo->prepare("DELETE FROM Bottiglie WHERE id=:id");
$p->execute([":id" => $_GET["id"]]);
echo json_encode(["ok" => true]);
break;
case "update":
$p = $pdo->prepare("UPDATE Bottiglie SET vino=:vino, cantina=:cantina, annata=:annata, volume=:volume, quantita=:quantita, note=:note, lastEdit=:lastEdit WHERE id=:id");
$e = $p->execute([
":id" => $_GET["id"],
":vino" => $_GET["vino"],
":cantina" => $_GET["cantina"],
":annata" => $_GET["annata"],
":volume" => $_GET["volume"],
":quantita" => $_GET["quantita"],
":note" => $_GET["note"],
":lastEdit" => time()
]);
echo json_encode(["ok" => $e]);
break;
case "add":
$p = $pdo->prepare("INSERT INTO Bottiglie(vino, cantina, annata, volume, quantita, note, createdAt, lastEdit) VALUES(:vino, :cantina, :annata, :volume, :quantita, :note, :createdAt, :lastEdit)");
$e = $p->execute([
":vino" => $_GET["vino"],
":cantina" => $_GET["cantina"],
":annata" => $_GET["annata"],
":volume" => $_GET["volume"],
":quantita" => $_GET["quantita"],
":note" => $_GET["note"],
":createdAt" => time(),
":lastEdit" => time()
]);
echo json_encode(["ok" => $e]);
break;
case "complete":
$type = $_GET["type"];
if (!in_array($type, ["vino", "cantina", "annata", "volume", "quantita", "note"])) {
echo json_encode([]);
break;
}
$p = $pdo->prepare("SELECT DISTINCT $type FROM Bottiglie");
$p->execute();
$list = [];
foreach ($p->fetchAll(PDO::FETCH_ASSOC) as $e) {
$list[] = $e[$type];
}
$userInput = $_GET["query"];
usort($list, function ($a, $b) use ($userInput) {
similar_text($userInput, $a, $percentA);
similar_text($userInput, $b, $percentB);
return $percentA === $percentB ? 0 : ($percentA > $percentB ? -1 : 1);
});
echo json_encode(array_slice($list, 0, 20));
break;
case "access":
echo json_encode(["ok" => password_verify($_GET["password"], file_get_contents("password.txt"))]);
break;
case "updatePassword":
file_put_contents("password.txt", password_hash($_GET["password"], PASSWORD_DEFAULT));
echo json_encode([]);
break;
}

BIN
database.db Normal file

Binary file not shown.

BIN
img/CantinaBotti.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 KiB

BIN
img/bin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
img/edit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

BIN
img/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
img/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
img/plus.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

BIN
img/view.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

311
index.html Normal file
View File

@ -0,0 +1,311 @@
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<link rel="stylesheet" href="style.css" />
<title>Cantina </title>
</head>
<body>
<div class="container mt-5">
<div class="header clearfix">
<nav>
<ul class="nav nav-pills float-right">
<li class="nav-item">
</li>
<li class="nav-item">
</li>
<li class="nav-item">
</li>
</ul>
</nav>
<h3 style="text-align: center;font-size: 50px;">Cantina Adamo<br></h3>
<br />
<div class="loggedInMenu">
<button type="button" class="btn btn-warning logout">Esci</button>
<button type="button" class="btn btn-success search">Ricerca</button>
<button type="button" class="btn btn-danger chpassBtn">Cambia password</button>
<form id="find">
<fieldset>
<!-- Form Name -->
<legend>Ricerca</legend>
<hr />
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text">Vino</span>
</div>
<input type="text" name="Svino" class="form-control" placeholder="" aria-label=""
aria-describedby="">
</div>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text">Cantina</span>
</div>
<input type="text" name="Scantina" class="form-control" placeholder="" aria-label=""
aria-describedby="">
</div>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text">Annata</span>
</div>
<input type="number" name="Sannata" class="form-control" placeholder="" aria-label=""
aria-describedby="">
</div>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text">Volume</span>
</div>
<input type="number" name="Svolume" class="form-control" placeholder="" aria-label=""
aria-describedby="">
<div class="input-group-prepend">
<span class="input-group-text">L</span>
</div>
</div>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text">Quantità</span>
</div>
<input type="number" min="0" step="1" name="Squantita" class="form-control" placeholder=""
aria-label="" aria-describedby="">
</div>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text">Note</span>
</div>
<textarea class="form-control" name="Snote" aria-label="With textarea"></textarea>
</div>
<br />
<button type="button" class="btn btn-danger resetR" style="float:left">Fine ricerca</button>
<button type="submit" class="btn btn-primary searchDo" style="float:right">Cerca</button>
</fieldset>
</form>
<hr />
Ordina per
<select class="form-select" name="orderBy" aria-label="Default select example">
<option selected value="id">#</option>
<option value="vino">Vino</option>
<option value="cantina">Cantina</option>
<option value="annata">Annata</option>
<option value="volume">Volume</option>
<option value="quantita">Quantità</option>
<option value="note">Note</option>
</select>
<select class="form-select" name="orderHow" aria-label="Default select example">
<option selected value="ASC">Crescente</option>
<option value="DESC">Decrescente</option>
</select>
</div>
</div>
</div>
</div> <!-- /container -->
<center>
<div class="container">
<div class="m-5">
<div id="tableview">
<table class="table table-striped">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Vino</th>
<th scope="col">Cantina</th>
<th scope="col">Annata</th>
<th scope="col">Volume</th>
<th scope="col">Quantità</th>
<th scope="col">Note</th>
<th scope="col"></th>
</tr>
</thead>
<tbody id="tabbody">
</tbody>
</table>
<p id="pageIndicator">Pagina a di b</p>
<nav aria-label="Page navigation example">
<ul class="pagination" id="tabnav">
<li class="page-item active"><a class="page-link" href="#">1</a></li>
<li class="page-item"><a class="page-link" href="#">2</a></li>
<li class="page-item"><a class="page-link" href="#">3</a></li>
</ul>
</nav>
<button type="button" class="btn btn-primary addP" style="float:right">Aggiungi</button>
</div>
<form class="form-horizontal" id="saveedit">
<fieldset>
<!-- Form Name -->
<legend>Aggiungi Bottiglia</legend>
<hr />
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text">Vino</span>
</div>
<input type="text" name="vino" class="form-control" placeholder="" aria-label=""
aria-describedby="">
</div>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text">Cantina</span>
</div>
<input type="text" name="cantina" class="form-control" placeholder="" aria-label=""
aria-describedby="">
</div>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text">Annata</span>
</div>
<input type="number" name="annata" class="form-control" placeholder="" aria-label=""
aria-describedby="">
</div>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text">Volume</span>
</div>
<input type="number" name="volume" class="form-control" placeholder="" aria-label=""
aria-describedby="">
<div class="input-group-prepend">
<span class="input-group-text">L</span>
</div>
</div>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text">Quantità</span>
</div>
<input type="number" min="0" step="1" name="quantita" class="form-control" placeholder=""
aria-label="" aria-describedby="">
</div>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text">Note</span>
</div>
<textarea class="form-control" name="note" aria-label="With textarea"></textarea>
</div>
<br />
<button type="button" class="btn btn-danger closeForm" style="float:left">Indietro</button>
<button type="submit" class="btn btn-primary" style="float:right">Salva</button>
</fieldset>
</form>
<div id="view">
<table class="table table-striped">
<tbody>
<tr>
<td><b>#</b></td>
<td id="Vid"></td>
</tr>
<tr>
<td><b>Vino</b></td>
<td id="Vvino"></td>
</tr>
<tr>
<td><b>Cantina</b></td>
<td id="Vcantina"></td>
</tr>
<tr>
<td><b>Annata</b></td>
<td id="Vannata"></td>
</tr>
<tr>
<td><b>Volume</b></td>
<td id="Vvolume"></td>
</tr>
<tr>
<td><b>Quantità</b></td>
<td id="Vquantita"></td>
</tr>
<tr>
<td><b>Note</b></td>
<td id="Vnote"></td>
</tr>
</tbody>
</table>
<button type="button" class="btn btn-warning closeForm" style="float:left">Chiudi</button>
<a href="#" data-action="delete" id="Vdelete" class="btn btn-danger" style="float:right">
<img class="actionIcon" src="img/bin.png">
</a>
</div>
<form id="passwAccess">
<fieldset>
<!-- Form Name -->
<legend>Accesso</legend>
<hr />
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text">Password</span>
</div>
<input type="password" name="password" class="form-control" placeholder="" aria-label=""
aria-describedby="">
</div>
<br />
<button type="submit" class="btn btn-success" style="float:right">Accedi</button>
</fieldset>
</form>
<form id="chpass">
<fieldset>
<!-- Form Name -->
<legend>Nuova password</legend>
<hr />
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text">Password</span>
</div>
<input type="password" name="Upassword" class="form-control" placeholder="" aria-label=""
aria-describedby="">
</div>
<br />
<button type="submit" class="btn btn-success" style="float:right">Salva</button>
<button type="button" class="btn btn-danger closeForm" style="float:left">Indietro</button>
</fieldset>
</form>
</div>
</div>
<p> © 2021 Mattia Mascarello, Giorgio Adamo</p><br>
</center>
<script src="https://code.jquery.com/jquery-3.4.1.min.js" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"
integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo"
crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"
integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6"
crossorigin="anonymous"></script>
<script
src="https://cdn.jsdelivr.net/gh/xcash/bootstrap-autocomplete@v2.3.7/dist/latest/bootstrap-autocomplete.min.js"></script>
<script src="main.js"></script>
</body>
</html>

240
main.js Normal file
View File

@ -0,0 +1,240 @@
function htmlEntities(str) {
return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}
$(".addP").click(function addItem() {
$("[name=vino]").val("");
$("[name=cantina]").val("");
$("[name=annata]").val("");
$("[name=volume]").val("");
$("[name=quantita]").val("");
$("[name=note]").val("");
$("#tableview").hide();
$("#saveedit").show();
formAction = "new";
});
$(".closeForm").click(function addItem() {
$("#tableview").show();
$("#saveedit").hide();
$("#view").hide();
$("#chpass").hide();
$("#find").hide();
});
$("#saveedit").hide();
var pageSize = 20, lastPage = 0, formAction = "new", lastId = 0;
function updateTable(pageNum) {
lastPage = pageNum;
$.getJSON("api.php?intent=list&pageSize=" + pageSize + "&pageNum=" + pageNum + ("&" + $("#find").serialize()).replaceAll("&S", "&") + "&orderBy=" + $("[name=orderBy]").val() + "&orderHow=" + $("[name=orderHow]").val(), function (data) {
$("#pageIndicator").text("Pagina " + (parseInt(pageNum) + 1) + " di " + data["pagTot"]);
$("#tabnav").html("");
for (let i = 0; i < data["pagTot"]; i++) {
var act = "";
if (i == pageNum) act = "active";
$("#tabnav").append('<li class="page-item ' + act + '"><a class="page-link" href="#" data-pageNum="' + i + '">' + (i + 1) + '</a></li>')
}
$("#tabbody").html("");
data["rows"].forEach(function iterate(value) {
$("#tabbody").append(`<tr>
<th scope="row">`+ htmlEntities(value["id"]) + `</th>
<td>`+ htmlEntities(value["vino"]) + `</td>
<td>`+ htmlEntities(value["cantina"]) + `</td>
<td>`+ htmlEntities(value["annata"]) + `</td>
<td>`+ htmlEntities(value["volume"]) + ` </td>
<td>`+ htmlEntities(value["quantita"]) + `</td>
<td>`+ htmlEntities(value["note"]) + `</td>
<td class="actionsCell">
<a href="#" data-action="view" data-id="`+ htmlEntities(value["id"]) + `">
<img class="actionIcon" src="img/view.png">
</a>
&nbsp;&nbsp;&nbsp;
<a href="#" data-action="edit" data-id="`+ htmlEntities(value["id"]) + `">
<img class="actionIcon" src="img/edit.png">
</a>
&nbsp;&nbsp;&nbsp;
<a href="#" data-action="delete" data-id="`+ htmlEntities(value["id"]) + `">
<img class="actionIcon" src="img/bin.png">
</a>
</td>
</tr>`);
});
});
}
updateTable(0);
$(document).delegate(".page-link", "click", function pageChange() {
updateTable($(this).attr("data-pageNum"));
});
$(document).delegate("[data-action=delete]", "click", function delElEvent() {
if (confirm("Eliminare? (questa azione è irreversibile)")) {
$.getJSON("api.php?intent=delete&id=" + $(this).attr("data-id"), function delSend() {
updateTable(lastPage);
$("#tableview").show();
$("#saveedit").hide();
$("#view").hide();
});
}
});
$(document).delegate("[data-action=edit]", "click", function editElEvent() {
lastId = $(this).attr("data-id");
$.getJSON("api.php?intent=get&id=" + $(this).attr("data-id"), function editEvent(data) {
formAction = "edit";
$("[name=vino]").val(data["vino"]);
$("[name=cantina]").val(data["cantina"]);
$("[name=annata]").val(data["annata"]);
$("[name=volume]").val(data["volume"]);
$("[name=quantita]").val(data["quantita"]);
$("[name=note]").val(data["note"]);
$("#tableview").hide();
$("#saveedit").show();
});
})
$(document).delegate("[data-action=view]", "click", function editElEvent() {
var idTemp = $(this).attr("data-id");
$.getJSON("api.php?intent=get&id=" + idTemp, function editEvent(data) {
$("#Vid").html(idTemp);
$("#Vvino").html(data["vino"]);
$("#Vcantina").html(data["cantina"]);
$("#Vannata").html(data["annata"]);
$("#Vvolume").html(data["volume"] + " ");
$("#Vquantita").html(data["quantita"]);
$("#Vnote").html(data["note"]);
$("#Vdelete").attr("data-id", idTemp);
$("#view").show();
$("#tableview").hide();
});
})
$("#saveedit").submit(function (e) {
e.preventDefault();
switch (formAction) {
case "edit":
$.getJSON("api.php?intent=update&id=" + lastId + "&" + $(this).serialize(), function editEvent(data) {
$("#tableview").show();
$("#saveedit").hide();
updateTable(lastPage);
});
break;
case "new":
$.getJSON("api.php?intent=add&" + $(this).serialize(), function editEvent(data) {
$("#tableview").show();
$("#saveedit").hide();
updateTable(lastPage);
});
break;
}
return false;
});
$('[name=vino]').autoComplete({
resolver: 'custom',
events: {
search: function (qry, callback) {
// let's do a custom ajax call
$.ajax(
'api.php',
{
data: { "intent": "complete", 'query': qry, "type": "vino" }
}
).done(function (res) {
callback(res)
});
}
}
});
$('[name=cantina]').autoComplete({
resolver: 'custom',
events: {
search: function (qry, callback) {
// let's do a custom ajax call
$.ajax(
'api.php',
{
data: { "intent": "complete", 'query': qry, "type": "cantina" }
}
).done(function (res) {
callback(res)
});
}
}
});
$('[name=annata]').autoComplete({
resolver: 'custom',
events: {
search: function (qry, callback) {
// let's do a custom ajax call
$.ajax(
'api.php',
{
data: { "intent": "complete", 'query': qry, "type": "annata" }
}
).done(function (res) {
callback(res)
});
}
}
});
$('[name=note]').autoComplete({
resolver: 'custom',
events: {
search: function (qry, callback) {
// let's do a custom ajax call
$.ajax(
'api.php',
{
data: { "intent": "complete", 'query': qry, "type": "note" }
}
).done(function (res) {
callback(res)
});
}
}
});
$("#passwAccess").submit(function access(e) {
e.preventDefault();
$.getJSON("api.php?intent=access&password=" + $("[name=password]").val(), function editEvent(data) {
if (data["ok"]) {
$("#passwAccess").hide();
$("#tableview").show();
$(".loggedInMenu").show();
}
else {
alert("Credenziali errate");
}
});
return false;
});
$("#chpass").submit(function access(e) {
e.preventDefault();
$.getJSON("api.php?intent=updatePassword&password=" + $("[name=Upassword]").val(), function editEvent(data) {
$("#chpass").hide();
$("#tableview").show();
});
return false;
});
$(".logout").click(function () {
location.reload();
})
$(".chpassBtn").click(function () {
$("#find").hide();
$("#tableview").hide();
$("#chpass").show();
});
$(".search").click(function () {
$("#find").show();
$("#tableview").show();
$("#chpass").hide();
});
$("#find").submit(function access(e) {
e.preventDefault();
updateTable(lastPage);
return false;
});
$(".resetR").click(function () {
$("#find")[0].reset();
$("#tableview").show();
$("#saveedit").hide();
$("#view").hide();
$("#chpass").hide();
$("#find").hide();
updateTable(lastPage);
});
$('[name=orderBy],[name=orderHow]').on('change', function () {
updateTable(lastPage);
});

1
password.txt Normal file
View File

@ -0,0 +1 @@
$2y$10$/f913leBtTSRuwfmCy2jiuw1C5QOZReY5i.spuNNrrfLPKx39KhOW

5
run.sh Normal file
View File

@ -0,0 +1,5 @@
php -S 127.0.0.1:8080 &
SERVER_PID=$!
firefox --kiosk http://127.0.0.1:8080
kill $SERVER_PID
pkill firefox

49
style.css Normal file
View File

@ -0,0 +1,49 @@
body {
background: url("img/CantinaBotti.jpg");
min-height: 500px;
/* Create the parallax scrolling effect */
background-attachment: fixed;
background-position: center;
background-repeat: no-repeat;
background-size: cover;
color: white;
}
table, tr, td, th {
color: white;
background-color: black;
opacity: 0.88;
}
.actionIcon {
filter: invert(1);
width: 20px;
}
.actionsCell {
text-align: center;
}
* {
scrollbar-width: none;
}
#view {
display: none;
}
#tableview {
display: none;
}
.loggedInMenu {
display: none;
}
#chpass {
display: none;
}
#find {
display: none;
}