Partially done
All checks were successful
Build and Deploy Zola Website / build_and_deploy (push) Successful in 16s
All checks were successful
Build and Deploy Zola Website / build_and_deploy (push) Successful in 16s
This commit is contained in:
parent
18c78e37a4
commit
2201430f59
12
TODO.md
12
TODO.md
@ -11,5 +11,13 @@
|
|||||||
- [ ] specific car view - charts, fuel records
|
- [ ] specific car view - charts, fuel records
|
||||||
- [ ] remove/edit fuel record
|
- [ ] remove/edit fuel record
|
||||||
|
|
||||||
- [ ] IndexDB
|
## Until release
|
||||||
- [ ]
|
- [ ] Sync offline data from locale storage
|
||||||
|
- [ ] Include kilometer state of an car
|
||||||
|
- [ ] More charts
|
||||||
|
- [ ] Average fuel conusption
|
||||||
|
- [ ] Kilometer state
|
||||||
|
- [ ] More cards
|
||||||
|
- [ ] Average fuel conusption in last 30 days
|
||||||
|
- [ ] Kilometer state in last 30 days`
|
||||||
|
- [ ] Offline navigation between dashboard and offline form
|
||||||
|
@ -52,10 +52,21 @@ class VehicleController extends Controller {
|
|||||||
|
|
||||||
|
|
||||||
public function edit() {
|
public function edit() {
|
||||||
// Edit vehicle (to be implemented later)
|
// TODO: Edit vehicle (to be implemented later)
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete() {
|
public function delete() {
|
||||||
// Delete vehicle (to be implemented later)
|
// TODO: Delete vehicle (to be implemented later)
|
||||||
|
}
|
||||||
|
|
||||||
|
public function api_get() {
|
||||||
|
if(!$_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
echo "Wrong method, use GET";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$vehicle = new Vehicle();
|
||||||
|
$result = $vehicle->getVehiclesByUser($_SESSION['user']['id']);
|
||||||
|
echo json_encode($result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
<link rel="stylesheet" href="/css/dashboard.css">
|
<link rel="stylesheet" href="/css/dashboard.css">
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="/css/form.css">
|
||||||
|
<link rel="stylesheet" href="/css/vehicle_create.css">
|
||||||
|
|
||||||
<section class="dashboard">
|
<section class="dashboard">
|
||||||
<h1>Welcome, <?= htmlspecialchars($_SESSION['user']['username']) ?>!</h1>
|
<h1>Welcome, <?= htmlspecialchars($_SESSION['user']['username']) ?>!</h1>
|
||||||
<div>
|
<div id="actions">
|
||||||
<a href="/refuel/create" class="btn-green">Add new refuel record!</a>
|
<a href="/refuel/create" class="btn-green">Add new refuel record!</a>
|
||||||
<a href="/vehicles" class="btn-primary">List all vehicles</a>
|
<a href="/vehicles" class="btn-primary">List all vehicles</a>
|
||||||
<button class="btn-primary" id="btn-offline-add">Add new refuel record OFFLINE</button>
|
<a class="btn-warning" id="btn-offline-add">Add new offline refuel record</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-wrapper">
|
<div class="card-wrapper">
|
||||||
<section class="card latest">
|
<section class="card latest">
|
||||||
|
@ -19,3 +19,21 @@
|
|||||||
width: 18rem;
|
width: 18rem;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#btn-offline-add {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-warning {
|
||||||
|
background-color: var(--clr-warning-muted);
|
||||||
|
border: var(--borderWidth-thin) solid var(--clr-border-danger);
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: var(--border-radious);
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-danger {
|
||||||
|
background-color: var(--clr-danger-muted);
|
||||||
|
border: var(--borderWidth-thin) solid var(--clr-border-danger);
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: var(--border-radious);
|
||||||
|
}
|
||||||
|
@ -20,7 +20,8 @@ h1 {
|
|||||||
.btn-secondary,
|
.btn-secondary,
|
||||||
.btn-tertiary,
|
.btn-tertiary,
|
||||||
.btn-green,
|
.btn-green,
|
||||||
.btn-danger {
|
.btn-danger,
|
||||||
|
.btn-warning {
|
||||||
background-color: var(--clr-primary);
|
background-color: var(--clr-primary);
|
||||||
padding: .5rem;
|
padding: .5rem;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
@ -46,3 +47,8 @@ h1 {
|
|||||||
background-color: var(--clr-danger-muted);
|
background-color: var(--clr-danger-muted);
|
||||||
border: var(--borderWidth-thin) solid var(--clr-border-danger);
|
border: var(--borderWidth-thin) solid var(--clr-border-danger);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-warning {
|
||||||
|
background-color: var(--clr-warning-muted);
|
||||||
|
border: var(--borderWidth-thin) solid var(--clr-border-danger);
|
||||||
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
--clr-tertiary: #151b23;
|
--clr-tertiary: #151b23;
|
||||||
--clr-green: #238636;
|
--clr-green: #238636;
|
||||||
--clr-danger-muted: #f851491a;
|
--clr-danger-muted: #f851491a;
|
||||||
|
--clr-warning-muted: #e08e455e;
|
||||||
|
|
||||||
--clr-link-blue: #4493f8;
|
--clr-link-blue: #4493f8;
|
||||||
--clr-light-blue: #39a2ae;
|
--clr-light-blue: #39a2ae;
|
||||||
|
@ -53,4 +53,9 @@ $router->group('/refuel', ['RequireAuth'], function ($router) {
|
|||||||
$router->add('/create', 'RefuelController@create');
|
$router->add('/create', 'RefuelController@create');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// API
|
||||||
|
$router->group('/api/v1', ['RequireAuth'], function ($router) {
|
||||||
|
$router->add('/vehicles/get', 'VehicleController@api_get');
|
||||||
|
});
|
||||||
|
|
||||||
$router->dispatch();
|
$router->dispatch();
|
||||||
|
@ -18,32 +18,152 @@ async function checkOnline() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showDashboard() {
|
||||||
|
const offline = document.querySelector(".offline");
|
||||||
|
offline.remove();
|
||||||
|
document.querySelector(".dashboard").style.display = "flex";
|
||||||
|
}
|
||||||
|
|
||||||
|
const btnOffline = document.querySelector("#btn-offline-add");
|
||||||
|
const divActions = document.querySelector("#actions");
|
||||||
|
let visible = true;
|
||||||
|
|
||||||
setInterval(async () => {
|
setInterval(async () => {
|
||||||
const isOnline = await checkOnline();
|
const isOnline = await checkOnline();
|
||||||
|
//const isOnline = false; // REMOVE!!!
|
||||||
if (!isOnline) {
|
if (!isOnline) {
|
||||||
console.log("OFFLINE!!!");
|
if (visible) {
|
||||||
}
|
console.log("OFFLINE!!!");
|
||||||
}, 5000);
|
Array.from(divActions.children).forEach(
|
||||||
|
(el) => (el.style.display = "none"),
|
||||||
|
);
|
||||||
|
visible = false;
|
||||||
|
|
||||||
const offbtn = document.querySelector("#btn-offline-add");
|
btnOffline.style.display = "block";
|
||||||
offbtn.addEventListener("click", (e) => {
|
|
||||||
|
document.querySelector(".hd-left").addEventListener("click", () => {
|
||||||
|
showDashboard();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (localStorage.getItem("refuelOfflineData")) {
|
||||||
|
btnOffline.textContent = "Sync data";
|
||||||
|
btnOffline.setAttribute("disabled", "disabled");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isOnline && !visible) {
|
||||||
|
console.log("BACK ONLINE!!!");
|
||||||
|
visible = true;
|
||||||
|
btnOffline.removeAttribute("disabled", "disabled");
|
||||||
|
// TODO: show buttons back, add sync button instead of record creation
|
||||||
|
// If user is in a process of adding new offline refuel record, let him finish
|
||||||
|
// Clear the local storage on each login
|
||||||
|
}
|
||||||
|
//}, 5000);
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
window.onload = async () => {
|
||||||
|
const rawData = await fetch("/api/v1/vehicles/get", {
|
||||||
|
method: "GET",
|
||||||
|
credentials: "include",
|
||||||
|
});
|
||||||
|
const data = await rawData.json();
|
||||||
|
console.log("Fetched:", data);
|
||||||
|
localStorage.setItem("vehicles", JSON.stringify(data));
|
||||||
|
console.log(JSON.parse(localStorage.getItem("vehicles")));
|
||||||
|
};
|
||||||
|
|
||||||
|
btnOffline.addEventListener("click", async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (btnOffline.textContent == "Sync data") {
|
||||||
|
if (!visible) {
|
||||||
|
alert("You're still offline. Try again later");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
let data = localStorage.getItem("refuelOfflineData");
|
||||||
|
if (!data) {
|
||||||
|
console.error("No offline data found");
|
||||||
|
alert("No offline data found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = JSON.parse(data);
|
||||||
|
const formData = new FormData();
|
||||||
|
|
||||||
|
for (const key in data) {
|
||||||
|
if (data.hasOwnProperty(key)) {
|
||||||
|
formData.append(key, data[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await fetch("/refuel/create", {
|
||||||
|
method: "POST",
|
||||||
|
body: formData,
|
||||||
|
credentials: "include",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(`Server error: ${res.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
localStorage.removeItem("refuelOfflineData");
|
||||||
|
location.reload();
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
alert("Something went wrong");
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
document.querySelector("section.dashboard").style.display = "none";
|
document.querySelector("section.dashboard").style.display = "none";
|
||||||
|
|
||||||
|
try {
|
||||||
|
vehicles = localStorage.getItem("vehicles");
|
||||||
|
if (vehicles === null) throw new Error("No data was saved locally");
|
||||||
|
vehicles = JSON.parse(vehicles);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
const offline = document.createElement("div");
|
||||||
|
offline.classList.add("offline");
|
||||||
|
offline.innerHTML = `
|
||||||
|
<div class="alert-danger">
|
||||||
|
<b>You're Offline</b>
|
||||||
|
<p>No data was saved locally, please try again later</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
document.querySelector("main").appendChild(offline);
|
||||||
|
// TODO: Add button to navigate back to offline dashboard
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const offline = document.createElement("div");
|
const offline = document.createElement("div");
|
||||||
offline.classList.add("offline");
|
offline.classList.add("offline");
|
||||||
offline.innerHTML = `
|
offline.innerHTML = `
|
||||||
<b>You're Offline</b>
|
<div class="alert-warning">
|
||||||
<p>You can create an fuel record locally on your device and sync it later</p>
|
<b>You're Offline</b>
|
||||||
|
<p>You can create an fuel record locally on your device and sync it later</p>
|
||||||
|
</div>
|
||||||
<section class="form">
|
<section class="form">
|
||||||
<h1 class="header-form"><?= $this->get('title') ?></h1>
|
<h1 class="header-form">Create offline record</h1>
|
||||||
<!-- <?php if ($this->get('error')): ?> -->
|
<!-- <?php if ($this->get('error')): ?> -->
|
||||||
<!-- <div class="error" style="color: red; margin-bottom: 1rem;"> -->
|
<!-- <div class="error" style="color: red; margin-bottom: 1rem;"> -->
|
||||||
<!-- <?= htmlspecialchars($this->get('error')) ?> -->
|
<!-- <?= htmlspecialchars($this->get('error')) ?> -->
|
||||||
<!-- </div> -->
|
<!-- </div> -->
|
||||||
<!-- <?php endif; ?> -->
|
<!-- <?php endif; ?> -->
|
||||||
<form method="POST" action="/refuel/create">
|
<form id="offline_refuel_add">
|
||||||
<label for="vehicle">Vehicle</label>
|
<label for="vehicle">Vehicle</label>
|
||||||
<select name="vehicle" id="vehicle">
|
<select name="vehicle" id="vehicle">
|
||||||
|
${vehicles
|
||||||
|
.map(
|
||||||
|
(el) =>
|
||||||
|
`<option value="${el.id}">${el.name} | ${el.registration_plate}</option>`,
|
||||||
|
)
|
||||||
|
.join("")}
|
||||||
<!-- <?php foreach ($this->get('vehicles') as $vehicle): ?> -->
|
<!-- <?php foreach ($this->get('vehicles') as $vehicle): ?> -->
|
||||||
<!-- <option value="<?= $vehicle['id'] ?>"><?= $vehicle['name'] . " | " . $vehicle['registration_plate'] ?></option> -->
|
<!-- <option value="<?= $vehicle['id'] ?>"><?= $vehicle['name'] . " | " . $vehicle['registration_plate'] ?></option> -->
|
||||||
<!-- <?php endforeach; ?> -->
|
<!-- <?php endforeach; ?> -->
|
||||||
@ -67,32 +187,54 @@ offbtn.addEventListener("click", (e) => {
|
|||||||
<!-- <?php endif; ?> -->
|
<!-- <?php endif; ?> -->
|
||||||
|
|
||||||
<label for="liters">Liters</label>
|
<label for="liters">Liters</label>
|
||||||
<input type="number" name="liters" id="liters" min="0" step=".01" value="<?= htmlspecialchars($_POST['liters'] ?? '0.0') ?>">
|
<input type="number" name="liters" id="liters" min="0" step=".01" value="0.0">
|
||||||
<!-- <?php if (isset($this->get('validationErrors')['liters'])): ?> -->
|
<!-- <?php if (isset($this->get('validationErrors')['liters'])): ?> -->
|
||||||
<!-- <small class="error"><?= $this->get('validationErrors')['liters'] ?></small> -->
|
<!-- <small class="error"><?= $this->get('validationErrors')['liters'] ?></small> -->
|
||||||
<!-- <?php endif; ?> -->
|
<!-- <?php endif; ?> -->
|
||||||
|
|
||||||
<label for="price_per_liter">Price per liter</label>
|
<label for="price_per_liter">Price per liter</label>
|
||||||
<input type="number" name="price_per_liter" id="price_per_liter" min="0" step=".01" value="<?= htmlspecialchars($_POST['price_per_liter'] ?? '0.0') ?>">
|
<input type="number" name="price_per_liter" id="price_per_liter" min="0" step=".01" value="0.0">
|
||||||
<!-- <?php if (isset($this->get('validationErrors')['price_per_liter'])): ?> -->
|
<!-- <?php if (isset($this->get('validationErrors')['price_per_liter'])): ?> -->
|
||||||
<!-- <small class="error"><?= $this->get('validationErrors')['price_per_liter'] ?></small> -->
|
<!-- <small class="error"><?= $this->get('validationErrors')['price_per_liter'] ?></small> -->
|
||||||
<!-- <?php endif; ?> -->
|
<!-- <?php endif; ?> -->
|
||||||
|
|
||||||
<label for="total_price">Total price</label>
|
<label for="total_price">Total price</label>
|
||||||
<input type="number" name="total_price" id="total_price" min="0" step=".01" value="<?= htmlspecialchars($_POST['total_price'] ?? '0.0') ?>">
|
<input type="number" name="total_price" id="total_price" min="0" step=".01" value="0.0">
|
||||||
<!-- <?php if (isset($this->get('validationErrors')['total_price'])): ?> -->
|
<!-- <?php if (isset($this->get('validationErrors')['total_price'])): ?> -->
|
||||||
<!-- <small class="error"><?= $this->get('validationErrors')['total_price'] ?></small> -->
|
<!-- <small class="error"><?= $this->get('validationErrors')['total_price'] ?></small> -->
|
||||||
<!-- <?php endif; ?> -->
|
<!-- <?php endif; ?> -->
|
||||||
|
|
||||||
<label for="note">Note</label>
|
<label for="note">Note</label>
|
||||||
<input type="text" name="note" id="note" value="<?= htmlspecialchars($_POST['note'] ?? '') ?>">
|
<input type="text" name="note" id="note">
|
||||||
<!-- <?php if (isset($this->get('validationErrors')['note'])): ?> -->
|
<!-- <?php if (isset($this->get('validationErrors')['note'])): ?> -->
|
||||||
<!-- <small class="error"><?= $this->get('validationErrors')['note'] ?></small> -->
|
<!-- <small class="error"><?= $this->get('validationErrors')['note'] ?></small> -->
|
||||||
<!-- <?php endif; ?> -->
|
<!-- <?php endif; ?> -->
|
||||||
|
|
||||||
<input type="submit" value="Create fuel record">
|
<input type="submit" id="btn-offline-submit" value="Create fuel record">
|
||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
`;
|
`;
|
||||||
document.querySelector("main").appendChild(offline);
|
document.querySelector("main").appendChild(offline);
|
||||||
|
const btnSubmit = document.querySelector("#btn-offline-submit");
|
||||||
|
btnSubmit.addEventListener("click", (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const formData = {
|
||||||
|
vehicle: document.querySelector("form#offline_refuel_add #vehicle").value,
|
||||||
|
fuel_type: document.querySelector("form#offline_refuel_add #fuel_type")
|
||||||
|
.value,
|
||||||
|
liters: document.querySelector("form#offline_refuel_add #liters").value,
|
||||||
|
price_per_liter: document.querySelector(
|
||||||
|
"form#offline_refuel_add #price_per_liter",
|
||||||
|
).value,
|
||||||
|
total_price: document.querySelector(
|
||||||
|
"form#offline_refuel_add #total_price",
|
||||||
|
).value,
|
||||||
|
note: document.querySelector("form#offline_refuel_add #note").value,
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log("formData", formData);
|
||||||
|
localStorage.setItem("refuelOfflineData", JSON.stringify(formData));
|
||||||
|
alert("Data was locally saved. Sync it later!");
|
||||||
|
showDashboard();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user