Compare commits
No commits in common. "master" and "fr/offline-record" have entirely different histories.
master
...
fr/offline
Binary file not shown.
Before Width: | Height: | Size: 26 KiB |
Binary file not shown.
Before Width: | Height: | Size: 92 KiB |
Binary file not shown.
Before Width: | Height: | Size: 36 KiB |
@ -53,13 +53,5 @@ php -S localhost:8000 -t ./public
|
|||||||
4. Track your fuel consumption and spending through the dashboard.
|
4. Track your fuel consumption and spending through the dashboard.
|
||||||
5. View detailed stats and graphs to analyze your driving habits.
|
5. View detailed stats and graphs to analyze your driving habits.
|
||||||
|
|
||||||
## Use case diagram
|
|
||||||

|
|
||||||
## Data logical model
|
|
||||||

|
|
||||||
|
|
||||||
## Class diagram
|
|
||||||

|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
This project is licensed under GPL3.0 and later. More information is available in the `LICENSE` file.
|
This project is licensed under GPL3.0 and later. More information is available in the `LICENSE` file.
|
||||||
|
35
TODO.md
35
TODO.md
@ -7,38 +7,9 @@
|
|||||||
|
|
||||||
## Core of the app
|
## Core of the app
|
||||||
- [ ] intro tutorial when no car exist or just dont show anything
|
- [ ] intro tutorial when no car exist or just dont show anything
|
||||||
- [x] change/set default car
|
- [ ] change/set default car
|
||||||
- [ ] specific car view - charts, fuel records
|
- [ ] specific car view - charts, fuel records
|
||||||
- [ ] remove/edit fuel record
|
- [ ] remove/edit fuel record
|
||||||
|
|
||||||
## Until release
|
- [ ] IndexDB
|
||||||
- [x] Sync offline data from locale storage
|
- [ ]
|
||||||
- [x] Include kilometer state of an car
|
|
||||||
- [ ] More charts
|
|
||||||
- [x] 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
|
|
||||||
|
|
||||||
## What has to be done
|
|
||||||
- [x] Vehicle delete
|
|
||||||
- [ ] intro tutorial when no car exist or just dont show anything
|
|
||||||
- [x] change/set default car
|
|
||||||
- [x] hide errors
|
|
||||||
|
|
||||||
## Nice to have
|
|
||||||
- [ ] specific car view - charts, fuel records
|
|
||||||
- [ ] remove/edit fuel record
|
|
||||||
- [x] Include kilometer state of an car
|
|
||||||
- [ ] More charts
|
|
||||||
- [x] 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
|
|
||||||
- [ ] Fix vehicle deletion - wrong redirect
|
|
||||||
- [ ] Update diagrams in README.md
|
|
||||||
|
|
||||||
|
@ -3,25 +3,29 @@ class DashboardController extends Controller {
|
|||||||
public function index() {
|
public function index() {
|
||||||
$vehicle = new Vehicle();
|
$vehicle = new Vehicle();
|
||||||
$vehicles = $vehicle->getVehiclesByUser($_SESSION['user']['id']);
|
$vehicles = $vehicle->getVehiclesByUser($_SESSION['user']['id']);
|
||||||
$default_car = $vehicle->getDefaultVehicle($_SESSION['user']['id']) ?? null;
|
|
||||||
|
$default_car = $vehicle->getDefaultVehicle($_SESSION['user']['id']);
|
||||||
|
|
||||||
$refuel = new Refuel();
|
$refuel = new Refuel();
|
||||||
$data = [
|
$data = [
|
||||||
"date" => [],
|
"date" => [],
|
||||||
"price" => [],
|
"price" => [],
|
||||||
"mileage" => [],
|
|
||||||
"liters" => []
|
|
||||||
];
|
];
|
||||||
$raw_data = $default_car ? $refuel->latest_data($default_car['id'], 0) : [];
|
$raw_data = $refuel->latest_data($default_car['id'], 5);
|
||||||
foreach($raw_data as $one) {
|
foreach($raw_data as $one) {
|
||||||
array_push($data['date'], date('d. m.', strtotime($one['created_at'])));
|
array_push($data['date'], date('d. m.', strtotime($one['created_at'])));
|
||||||
array_push($data['price'], $one['price_per_liter']);
|
array_push($data['price'], $one['price_per_liter']);
|
||||||
array_push($data['mileage'], $one['mileage']);
|
|
||||||
array_push($data['liters'], $one['liters']);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$latest_data = $default_car ? $refuel->latest_one($default_car['id']) : [];
|
$latest_record = [
|
||||||
$latest_record = !empty($latest_data) ? $latest_data[0] : null;
|
'name',
|
||||||
|
'liters',
|
||||||
|
'price_per_liter',
|
||||||
|
'total_price',
|
||||||
|
'created_at'
|
||||||
|
];
|
||||||
|
|
||||||
|
$latest_record = $refuel->latest_one($_SESSION['user']['id'])[0];
|
||||||
|
|
||||||
$this->view('dashboard/index', [
|
$this->view('dashboard/index', [
|
||||||
'title' => 'Dashboard',
|
'title' => 'Dashboard',
|
||||||
|
@ -18,7 +18,6 @@ class RefuelController extends Controller {
|
|||||||
$liters = $_POST['liters'] ?? '';
|
$liters = $_POST['liters'] ?? '';
|
||||||
$price_per_liter = $_POST['price_per_liter'] ?? '';
|
$price_per_liter = $_POST['price_per_liter'] ?? '';
|
||||||
$total_price = $_POST['total_price'] ?? '';
|
$total_price = $_POST['total_price'] ?? '';
|
||||||
$mileage = $_POST['mileage'] ?? '';
|
|
||||||
$note = $_POST['note'] ?? '';
|
$note = $_POST['note'] ?? '';
|
||||||
|
|
||||||
$validator = new Validator();
|
$validator = new Validator();
|
||||||
@ -30,7 +29,6 @@ class RefuelController extends Controller {
|
|||||||
$validator->number('liters', $liters);
|
$validator->number('liters', $liters);
|
||||||
$validator->number('price_per_liter', $price_per_liter);
|
$validator->number('price_per_liter', $price_per_liter);
|
||||||
$validator->number('total_price', $total_price);
|
$validator->number('total_price', $total_price);
|
||||||
$validator->number('mileage', $mileage);
|
|
||||||
|
|
||||||
if (round($liters * $price_per_liter, 2) != $total_price) {
|
if (round($liters * $price_per_liter, 2) != $total_price) {
|
||||||
$validator->setErrors(["total_price" => "Price calculation is wrong"]);
|
$validator->setErrors(["total_price" => "Price calculation is wrong"]);
|
||||||
@ -46,7 +44,6 @@ class RefuelController extends Controller {
|
|||||||
'validationErrors' => $validator->errors() ?: [],
|
'validationErrors' => $validator->errors() ?: [],
|
||||||
'vehicles' => $vehicles,
|
'vehicles' => $vehicles,
|
||||||
'title' => 'New refuel record',
|
'title' => 'New refuel record',
|
||||||
'status' => '400'
|
|
||||||
]);
|
]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -60,7 +57,6 @@ class RefuelController extends Controller {
|
|||||||
'liters' => $liters,
|
'liters' => $liters,
|
||||||
'price_per_liter' => $price_per_liter,
|
'price_per_liter' => $price_per_liter,
|
||||||
'total_price' => $total_price,
|
'total_price' => $total_price,
|
||||||
'mileage' => $mileage,
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($result === true) {
|
if ($result === true) {
|
||||||
|
@ -30,21 +30,17 @@ class VehicleController extends Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$vehicle = new Vehicle();
|
$vehicle = new Vehicle();
|
||||||
$default_vehicle = $vehicle->getDefaultVehicle($_SESSION['user']['id']);
|
|
||||||
$is_default = $default_vehicle ? 0 : 1;
|
|
||||||
|
|
||||||
$result = $vehicle->create([
|
$result = $vehicle->create([
|
||||||
'name' => $name,
|
'name' => $name,
|
||||||
'registration_plate' => strtoupper($registration_plate),
|
'registration_plate' => strtoupper($registration_plate),
|
||||||
'fuel_type' => $fuel_type,
|
'fuel_type' => $fuel_type,
|
||||||
'note' => $note,
|
'note' => $note,
|
||||||
'user_id' => $_SESSION['user']['id'],
|
'user_id' => $_SESSION['user']['id'],
|
||||||
'is_default' => $is_default
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
||||||
if ($result === true) {
|
if ($result === true) {
|
||||||
$this->redirect('/');
|
$this->redirect('/vehicles');
|
||||||
} else {
|
} else {
|
||||||
$this->view('vehicles/create', ['title' => 'Create vehicle', 'error' => $result, 'validationErrors' => []] );
|
$this->view('vehicles/create', ['title' => 'Create vehicle', 'error' => $result, 'validationErrors' => []] );
|
||||||
}
|
}
|
||||||
@ -56,49 +52,10 @@ class VehicleController extends Controller {
|
|||||||
|
|
||||||
|
|
||||||
public function edit() {
|
public function edit() {
|
||||||
// TODO: Edit vehicle (to be implemented later)
|
// Edit vehicle (to be implemented later)
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete() {
|
public function delete() {
|
||||||
if(!$_SERVER['REQUEST_METHOD'] === 'POST') {
|
// Delete vehicle (to be implemented later)
|
||||||
echo "Wrong method";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Validate the request
|
|
||||||
$vehicle_id = $_POST['vehicle_id'];
|
|
||||||
|
|
||||||
$vehicle = new Vehicle();
|
|
||||||
$result = $vehicle->delete($vehicle_id, $_SESSION['user']['id']);
|
|
||||||
|
|
||||||
if($result != true) {
|
|
||||||
echo "Something went wrong";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
header("Location: /vehicles");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setDefault() {
|
|
||||||
$vehicle = new Vehicle();
|
|
||||||
// TODO: Validate the request
|
|
||||||
$result = $vehicle->setDefaultVehicle($_POST['vehicle_id'], $_SESSION['user']['id']);
|
|
||||||
if($result != true) {
|
|
||||||
echo "Something went wrong";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
header("Location: /");
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,12 +10,12 @@ class Refuel {
|
|||||||
public function create($data) {
|
public function create($data) {
|
||||||
try{
|
try{
|
||||||
$stmt = $this->db->prepare("
|
$stmt = $this->db->prepare("
|
||||||
INSERT INTO refueling_records (user_id, vehicle_id, fuel_type, note, liters, price_per_liter, total_price, mileage, created_at)
|
INSERT INTO refueling_records (user_id, vehicle_id, fuel_type, note, liters, price_per_liter, total_price, created_at)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, NOW())
|
VALUES (?, ?, ?, ?, ?, ?, ?, NOW())
|
||||||
");
|
");
|
||||||
|
|
||||||
$stmt->bind_param(
|
$stmt->bind_param(
|
||||||
"iissdddi",
|
"iissddd",
|
||||||
$data['user_id'],
|
$data['user_id'],
|
||||||
$data['vehicle_id'],
|
$data['vehicle_id'],
|
||||||
$data['fuel_type'],
|
$data['fuel_type'],
|
||||||
@ -23,7 +23,6 @@ class Refuel {
|
|||||||
$data['liters'],
|
$data['liters'],
|
||||||
$data['price_per_liter'],
|
$data['price_per_liter'],
|
||||||
$data['total_price'],
|
$data['total_price'],
|
||||||
$data['mileage'],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($stmt->execute()) {
|
if ($stmt->execute()) {
|
||||||
@ -38,24 +37,15 @@ class Refuel {
|
|||||||
|
|
||||||
public function latest_data($vehicle_id, $record_count) {
|
public function latest_data($vehicle_id, $record_count) {
|
||||||
try {
|
try {
|
||||||
$sql = "
|
$stmt = $this->db->prepare("
|
||||||
SELECT `liters`, `price_per_liter`, `total_price`, `mileage`, `created_at`
|
SELECT `liters`, `price_per_liter`, `total_price`, `created_at`
|
||||||
FROM `refueling_records`
|
FROM `refueling_records`
|
||||||
WHERE `vehicle_id` = ?
|
WHERE `vehicle_id` = ?
|
||||||
ORDER BY created_at DESC";
|
ORDER BY created_at DESC
|
||||||
|
LIMIT ?;
|
||||||
|
");
|
||||||
|
|
||||||
if ($record_count > 0) {
|
|
||||||
$sql .= " LIMIT ?";
|
|
||||||
}
|
|
||||||
|
|
||||||
$stmt = $this->db->prepare($sql);
|
|
||||||
|
|
||||||
if ($record_count > 0) {
|
|
||||||
$stmt->bind_param("ii", $vehicle_id, $record_count);
|
$stmt->bind_param("ii", $vehicle_id, $record_count);
|
||||||
} else {
|
|
||||||
$stmt->bind_param("i", $vehicle_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($stmt->execute()) {
|
if ($stmt->execute()) {
|
||||||
$result = $stmt->get_result();
|
$result = $stmt->get_result();
|
||||||
$data = $result->fetch_all(MYSQLI_ASSOC);
|
$data = $result->fetch_all(MYSQLI_ASSOC);
|
||||||
@ -69,35 +59,24 @@ class Refuel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function latest_one($vehicle_id, $record_count = 1) {
|
public function latest_one($user_id, $record_count = 1) {
|
||||||
try {
|
try {
|
||||||
$sql = "
|
$stmt = $this->db->prepare("
|
||||||
SELECT
|
SELECT
|
||||||
`r`.`vehicle_id`,
|
`r`.`vehicle_id`,
|
||||||
`v`.`name` AS `vehicle_name`,
|
`v`.`name` AS `vehicle_name`,
|
||||||
`r`.`liters`,
|
`r`.`liters`,
|
||||||
`r`.`price_per_liter`,
|
`r`.`price_per_liter`,
|
||||||
`r`.`total_price`,
|
`r`.`total_price`,
|
||||||
`r`.`mileage`,
|
|
||||||
`r`.`note`,
|
|
||||||
`r`.`created_at`
|
`r`.`created_at`
|
||||||
FROM `refueling_records` AS `r`
|
FROM `refueling_records` AS `r`
|
||||||
JOIN `vehicles` AS `v` ON `r`.`vehicle_id` = `v`.`id`
|
JOIN `vehicles` AS `v` ON `r`.`vehicle_id` = `v`.`id`
|
||||||
WHERE `r`.`vehicle_id` = ?
|
WHERE `r`.`user_id` = ?
|
||||||
ORDER BY `r`.`created_at` DESC";
|
ORDER BY `r`.`created_at` DESC
|
||||||
|
LIMIT ?;
|
||||||
if ($record_count > 0) {
|
");
|
||||||
$sql .= " LIMIT ?";
|
|
||||||
}
|
|
||||||
|
|
||||||
$stmt = $this->db->prepare($sql);
|
|
||||||
|
|
||||||
if ($record_count > 0) {
|
|
||||||
$stmt->bind_param("ii", $vehicle_id, $record_count);
|
|
||||||
} else {
|
|
||||||
$stmt->bind_param("i", $vehicle_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
$stmt->bind_param("ii", $user_id, $record_count);
|
||||||
if ($stmt->execute()) {
|
if ($stmt->execute()) {
|
||||||
$result = $stmt->get_result();
|
$result = $stmt->get_result();
|
||||||
$data = $result->fetch_all(MYSQLI_ASSOC);
|
$data = $result->fetch_all(MYSQLI_ASSOC);
|
||||||
|
@ -29,7 +29,7 @@ class User {
|
|||||||
|
|
||||||
$hashedPassword = password_hash($password, PASSWORD_BCRYPT);
|
$hashedPassword = password_hash($password, PASSWORD_BCRYPT);
|
||||||
|
|
||||||
$stmt = $this->db->prepare("INSERT INTO users (username, email, password, created_at) VALUES (?, ?, ?, NOW())");
|
$stmt = $this->db->prepare("INSERT INTO users (username, email, password, points, created_at) VALUES (?, ?, ?, 0, NOW())");
|
||||||
$stmt->bind_param("sss", $username, $email, $hashedPassword);
|
$stmt->bind_param("sss", $username, $email, $hashedPassword);
|
||||||
|
|
||||||
if ($stmt->execute()) {
|
if ($stmt->execute()) {
|
||||||
|
@ -10,18 +10,17 @@ class Vehicle {
|
|||||||
public function create($data) {
|
public function create($data) {
|
||||||
try{
|
try{
|
||||||
$stmt = $this->db->prepare("
|
$stmt = $this->db->prepare("
|
||||||
INSERT INTO vehicles (user_id, name, registration_plate, fuel_type, note, is_default, created_at)
|
INSERT INTO vehicles (user_id, name, registration_plate, fuel_type, note, created_at)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, NOW())
|
VALUES (?, ?, ?, ?, ?, NOW())
|
||||||
");
|
");
|
||||||
|
|
||||||
$stmt->bind_param(
|
$stmt->bind_param(
|
||||||
"issssi",
|
"issss",
|
||||||
$data['user_id'],
|
$data['user_id'],
|
||||||
$data['name'],
|
$data['name'],
|
||||||
$data['registration_plate'],
|
$data['registration_plate'],
|
||||||
$data['fuel_type'],
|
$data['fuel_type'],
|
||||||
$data['note'],
|
$data['note'],
|
||||||
$data['is_default'],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($stmt->execute()) {
|
if ($stmt->execute()) {
|
||||||
@ -35,7 +34,7 @@ class Vehicle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getVehiclesByUser($user_id) {
|
public function getVehiclesByUser($user_id) {
|
||||||
$stmt = $this->db->prepare("SELECT id, name, registration_plate, fuel_type, note, is_default, created_at FROM vehicles WHERE user_id = ?");
|
$stmt = $this->db->prepare("SELECT id, name, registration_plate, fuel_type, note, created_at FROM vehicles WHERE user_id = ?");
|
||||||
$stmt->bind_param("i", $user_id);
|
$stmt->bind_param("i", $user_id);
|
||||||
$stmt->execute();
|
$stmt->execute();
|
||||||
$result = $stmt->get_result();
|
$result = $stmt->get_result();
|
||||||
@ -61,61 +60,4 @@ class Vehicle {
|
|||||||
|
|
||||||
return $result->fetch_assoc();
|
return $result->fetch_assoc();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setDefaultVehicle($vehicle_id, $user_id) {
|
|
||||||
try {
|
|
||||||
$this->db->begin_transaction();
|
|
||||||
|
|
||||||
$stmt = $this->db->prepare("
|
|
||||||
UPDATE `vehicles`
|
|
||||||
SET `is_default` = 0
|
|
||||||
WHERE `user_id` = ? AND `is_default` = 1
|
|
||||||
");
|
|
||||||
$stmt->bind_param("i", $user_id);
|
|
||||||
$stmt->execute();
|
|
||||||
$stmt->close();
|
|
||||||
|
|
||||||
$stmt = $this->db->prepare("
|
|
||||||
UPDATE `vehicles`
|
|
||||||
SET `is_default` = 1
|
|
||||||
WHERE `id` = ? AND `user_id` = ?
|
|
||||||
");
|
|
||||||
$stmt->bind_param("ii", $vehicle_id, $user_id);
|
|
||||||
|
|
||||||
if ($stmt->execute()) {
|
|
||||||
$this->db->commit();
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
$this->db->rollback();
|
|
||||||
return "Error: " . $stmt->error;
|
|
||||||
}
|
|
||||||
} catch (mysqli_sql_exception $e) {
|
|
||||||
$this->db->rollback();
|
|
||||||
return $e->getMessage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function delete($vehicle_id, $user_id) {
|
|
||||||
try {
|
|
||||||
$stmt = $this->db->prepare("SELECT id FROM vehicles WHERE id = ? AND user_id = ?");
|
|
||||||
$stmt->bind_param("ii", $vehicle_id, $user_id);
|
|
||||||
$stmt->execute();
|
|
||||||
$result = $stmt->get_result();
|
|
||||||
|
|
||||||
if ($result->num_rows === 0) {
|
|
||||||
return "Error: Unauthorized action or vehicle not found.";
|
|
||||||
}
|
|
||||||
|
|
||||||
$stmt = $this->db->prepare("DELETE FROM vehicles WHERE id = ?");
|
|
||||||
$stmt->bind_param("i", $vehicle_id);
|
|
||||||
|
|
||||||
if ($stmt->execute()) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return "Error: " . $stmt->error;
|
|
||||||
}
|
|
||||||
} catch (mysqli_sql_exception $e) {
|
|
||||||
return $e->getMessage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,11 @@
|
|||||||
<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>
|
||||||
<?php if(!isset($data['default_car'])): ?>
|
<div>
|
||||||
|
<a href="/refuel/create" class="btn-green">Add new refuel record!</a>
|
||||||
<div id="intro">
|
|
||||||
<a href="/vehicles/create">Create your first vehicle</a>
|
|
||||||
</div>
|
|
||||||
<?php elseif (isset($data['latest_record'])): ?>
|
|
||||||
|
|
||||||
<div id="actions">
|
|
||||||
<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>
|
||||||
<a class="btn-warning" id="btn-offline-add">Add new offline refuel record</a>
|
<button class="btn-primary" id="btn-offline-add">Add new refuel record OFFLINE</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-wrapper">
|
<div class="card-wrapper">
|
||||||
<section class="card latest">
|
<section class="card latest">
|
||||||
@ -30,12 +20,6 @@
|
|||||||
<p><?= $data['latest_record']['price_per_liter'] ?>,-/liter</p>
|
<p><?= $data['latest_record']['price_per_liter'] ?>,-/liter</p>
|
||||||
<b>Total price:</b>
|
<b>Total price:</b>
|
||||||
<p><?= $data['latest_record']['total_price'] ?>,-</p>
|
<p><?= $data['latest_record']['total_price'] ?>,-</p>
|
||||||
<b>Mileage:</b>
|
|
||||||
<p><?= $data['latest_record']['mileage'] ?> km</p>
|
|
||||||
<?php if (isset($data['latest_record']['note'])): ?>
|
|
||||||
<b>Note:</b>
|
|
||||||
<p><?= $data['latest_record']['note'] ?></p>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@ -58,25 +42,7 @@
|
|||||||
<p><?= $data['default_car']['name'] . " | " . $data['default_car']['registration_plate']?></p>
|
<p><?= $data['default_car']['name'] . " | " . $data['default_car']['registration_plate']?></p>
|
||||||
<canvas id="chart-gas-price"></canvas>
|
<canvas id="chart-gas-price"></canvas>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="card history-graph">
|
|
||||||
<h2>Average fuel consumption</h2>
|
|
||||||
<hr>
|
|
||||||
<p><?= $data['default_car']['name'] . " | " . $data['default_car']['registration_plate']?></p>
|
|
||||||
<b id="avg-fl-cnsmp"></b>
|
|
||||||
</section>
|
|
||||||
</div>
|
</div>
|
||||||
<?php else: ?>
|
|
||||||
<div id="actions">
|
|
||||||
<a href="/refuel/create" class="btn-green">Add new refuel record</a>
|
|
||||||
<a href="/vehicles" class="btn-primary">List all vehicles</a>
|
|
||||||
<a class="btn-warning" id="btn-offline-add">Add new offline refuel record</a>
|
|
||||||
</div>
|
|
||||||
<div class="alert-warning">
|
|
||||||
<p>Default vehicle <b><i><?= $data['default_car']['name'] ?></i></b> doesn't have any refuel record yet.</p>
|
|
||||||
<p>Select another vehicle or create first refuel record.</p>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
</section>
|
</section>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||||
<script>
|
<script>
|
||||||
@ -95,29 +61,4 @@
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script>
|
|
||||||
const data2 = <?= json_encode($data['date_price_data']); ?>;
|
|
||||||
let cnt_ltr = 0;
|
|
||||||
let last_ltr = 0;
|
|
||||||
let last_km = 0;
|
|
||||||
let first_km = 0;
|
|
||||||
|
|
||||||
for (let i = 0; i < data2['liters'].length; i++) {
|
|
||||||
if(i === 0) {
|
|
||||||
first_km = data2['mileage'][i]
|
|
||||||
continue; // skip bcs were expecting that this was first full tank refuel so this is base value
|
|
||||||
}
|
|
||||||
cnt_ltr += data2['liters'][i]
|
|
||||||
last_ltr = data2['liters'][i]
|
|
||||||
last_km = data2['mileage'][i]
|
|
||||||
}
|
|
||||||
|
|
||||||
// cnt_ltr -= last_ltr // this would be used if we would track consumption from 0km
|
|
||||||
last_km -= first_km
|
|
||||||
|
|
||||||
document.querySelector("#avg-fl-cnsmp").textContent =
|
|
||||||
(cnt_ltr/last_km*100).toFixed(1) + " l/100km";
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script defer src="/js/offline-records.js"></script>
|
<script defer src="/js/offline-records.js"></script>
|
||||||
|
@ -52,12 +52,6 @@
|
|||||||
<small class="error"><?= $this->get('validationErrors')['total_price'] ?></small>
|
<small class="error"><?= $this->get('validationErrors')['total_price'] ?></small>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<label for="mileage">Mileage</label>
|
|
||||||
<input type="number" name="mileage" id="mileage" min="0" step="1" value="<?= htmlspecialchars($_POST['mileage'] ?? '0') ?>">
|
|
||||||
<?php if (isset($this->get('validationErrors')['mileage'])): ?>
|
|
||||||
<small class="error"><?= $this->get('validationErrors')['mileage'] ?></small>
|
|
||||||
<?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" value="<?= htmlspecialchars($_POST['note'] ?? '') ?>">
|
||||||
<?php if (isset($this->get('validationErrors')['note'])): ?>
|
<?php if (isset($this->get('validationErrors')['note'])): ?>
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
<p>No vehicles yet. <a href="/vehicles/create">Add your first vehicle</a>.</p>
|
<p>No vehicles yet. <a href="/vehicles/create">Add your first vehicle</a>.</p>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<div class="btn-wrapper">
|
<div class="btn-wrapper">
|
||||||
<a href="/vehicles/create" class="btn-green">Add new vehicle</a>
|
<a href="/vehicles/create" class="btn-green">Add new vehicle!</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="vehicle-wrapper">
|
<div class="vehicle-wrapper">
|
||||||
<?php foreach ($this->get('vehicles') as $vehicle): ?>
|
<?php foreach ($this->get('vehicles') as $vehicle): ?>
|
||||||
@ -13,21 +13,9 @@
|
|||||||
<p><?= htmlspecialchars($vehicle['registration_plate']) ?></p>
|
<p><?= htmlspecialchars($vehicle['registration_plate']) ?></p>
|
||||||
<p><?= htmlspecialchars($vehicle['fuel_type']) ?></p>
|
<p><?= htmlspecialchars($vehicle['fuel_type']) ?></p>
|
||||||
<p><?= htmlspecialchars($vehicle['note'] ?? "") ?></p>
|
<p><?= htmlspecialchars($vehicle['note'] ?? "") ?></p>
|
||||||
|
<div class="actions">
|
||||||
<div class="vehicle-actions">
|
<a href="/vehicles/edit?id=<?= $vehicle['id'] ?>">Edit</a>
|
||||||
<br>
|
<a href="/vehicles/delete?id=<?= $vehicle['id'] ?>" onclick="return confirm('Are you sure you want to delete this habit?')">Delete</a>
|
||||||
<form method="POST" action="/vehicles/delete">
|
|
||||||
<input type="number" name="vehicle_id" value="<?= $vehicle['id'] ?>" style="display: none">
|
|
||||||
<input type="submit" value="Delete vehicle" class="btn-danger">
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<?php if(!$vehicle['is_default']): ?>
|
|
||||||
<br>
|
|
||||||
<form method="POST" action="/vehicles/default">
|
|
||||||
<input type="number" name="vehicle_id" value="<?= $vehicle['id'] ?>" style="display: none">
|
|
||||||
<input type="submit" value="Set as default" class="btn-primary">
|
|
||||||
</form>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
|
@ -63,6 +63,7 @@ class Database {
|
|||||||
username VARCHAR(50) NOT NULL,
|
username VARCHAR(50) NOT NULL,
|
||||||
email VARCHAR(100) NOT NULL UNIQUE,
|
email VARCHAR(100) NOT NULL UNIQUE,
|
||||||
password VARCHAR(255) NOT NULL,
|
password VARCHAR(255) NOT NULL,
|
||||||
|
points INT DEFAULT 0,
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
) ENGINE=InnoDB;";
|
) ENGINE=InnoDB;";
|
||||||
|
|
||||||
@ -98,7 +99,6 @@ class Database {
|
|||||||
liters DOUBLE(10, 2) NOT NULL,
|
liters DOUBLE(10, 2) NOT NULL,
|
||||||
price_per_liter DOUBLE(10, 2) NOT NULL,
|
price_per_liter DOUBLE(10, 2) NOT NULL,
|
||||||
total_price DOUBLE(10, 2) NOT NULL,
|
total_price DOUBLE(10, 2) NOT NULL,
|
||||||
mileage INT UNSIGNED NULL,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
FOREIGN KEY (vehicle_id) REFERENCES vehicles(id) ON DELETE CASCADE,
|
FOREIGN KEY (vehicle_id) REFERENCES vehicles(id) ON DELETE CASCADE,
|
||||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||||
|
@ -7,11 +7,6 @@ class View
|
|||||||
// Store the data
|
// Store the data
|
||||||
$this->data = $data;
|
$this->data = $data;
|
||||||
|
|
||||||
// check for status code in data
|
|
||||||
if(isset($this->data['status'])){
|
|
||||||
http_response_code($this->data['status']);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Capture the view content
|
// Capture the view content
|
||||||
ob_start();
|
ob_start();
|
||||||
require_once views . $view . '.php';
|
require_once views . $view . '.php';
|
||||||
|
@ -6,10 +6,6 @@ services:
|
|||||||
MARIADB_ROOT_PASSWORD: root
|
MARIADB_ROOT_PASSWORD: root
|
||||||
ports:
|
ports:
|
||||||
- 3306:3306
|
- 3306:3306
|
||||||
volumes:
|
|
||||||
- fuelstats_mariadb_data:/var/lib/mysql
|
|
||||||
networks:
|
|
||||||
- fuelstats-network
|
|
||||||
profiles: ["prod", "dev"]
|
profiles: ["prod", "dev"]
|
||||||
|
|
||||||
phpmyadmin:
|
phpmyadmin:
|
||||||
@ -19,11 +15,6 @@ services:
|
|||||||
- 8080:80
|
- 8080:80
|
||||||
environment:
|
environment:
|
||||||
- PMA_ARBITRARY=1
|
- PMA_ARBITRARY=1
|
||||||
- PMA_HOST=mariadb
|
|
||||||
depends_on:
|
|
||||||
- mariadb
|
|
||||||
networks:
|
|
||||||
- fuelstats-network
|
|
||||||
profiles: ["dev"]
|
profiles: ["dev"]
|
||||||
|
|
||||||
fuelstats:
|
fuelstats:
|
||||||
@ -37,13 +28,4 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
- mariadb
|
- mariadb
|
||||||
restart: on-failure:2
|
restart: on-failure:2
|
||||||
networks:
|
|
||||||
- fuelstats-network
|
|
||||||
profiles: ["prod"]
|
profiles: ["prod"]
|
||||||
|
|
||||||
networks:
|
|
||||||
fuelstats-network:
|
|
||||||
driver: bridge
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
fuelstats_mariadb_data:
|
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
margin-top: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
@ -18,21 +19,3 @@
|
|||||||
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,8 +20,7 @@ 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;
|
||||||
@ -47,13 +46,3 @@ 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
#actions {
|
|
||||||
padding-top: 1rem;
|
|
||||||
padding-bottom: 2rem;
|
|
||||||
}
|
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
--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;
|
||||||
|
@ -27,10 +27,3 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vehicle-actions {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
gap: .5rem;
|
|
||||||
margin-top: 1rem;
|
|
||||||
}
|
|
||||||
|
@ -46,17 +46,11 @@ $router->group('/vehicles', ['RequireAuth'], function ($router) {
|
|||||||
$router->add('', 'VehicleController@index');
|
$router->add('', 'VehicleController@index');
|
||||||
$router->add('/create', 'VehicleController@create');
|
$router->add('/create', 'VehicleController@create');
|
||||||
$router->add('/edit/{id}', 'VehicleController@edit');
|
$router->add('/edit/{id}', 'VehicleController@edit');
|
||||||
$router->add('/delete', 'VehicleController@delete');
|
$router->add('/delete/{id}', 'VehicleController@delete');
|
||||||
$router->add('/default', 'VehicleController@setDefault');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$router->group('/refuel', ['RequireAuth'], function ($router) {
|
$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,163 +18,39 @@ 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) {
|
||||||
if (visible) {
|
|
||||||
console.log("OFFLINE!!!");
|
console.log("OFFLINE!!!");
|
||||||
Array.from(divActions.children).forEach(
|
|
||||||
(el) => (el.style.display = "none"),
|
|
||||||
);
|
|
||||||
visible = false;
|
|
||||||
|
|
||||||
btnOffline.style.display = "block";
|
|
||||||
|
|
||||||
document.querySelector(".hd-left").addEventListener("click", () => {
|
|
||||||
showDashboard();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
if (localStorage.getItem("refuelOfflineData")) {
|
const offbtn = document.querySelector("#btn-offline-add");
|
||||||
Array.from(divActions.children).forEach(
|
offbtn.addEventListener("click", (e) => {
|
||||||
(el) => (el.style.display = "none"),
|
|
||||||
);
|
|
||||||
btnOffline.style.display = "block";
|
|
||||||
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) {
|
|
||||||
if (res.status == 400) {
|
|
||||||
const html = await res.text();
|
|
||||||
|
|
||||||
document.open();
|
|
||||||
document.write(html);
|
|
||||||
document.close();
|
|
||||||
|
|
||||||
localStorage.removeItem("refuelOfflineData");
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
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");
|
const offline = document.createElement("div");
|
||||||
offline.classList.add("offline");
|
offline.classList.add("offline");
|
||||||
offline.innerHTML = `
|
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");
|
|
||||||
offline.classList.add("offline");
|
|
||||||
offline.innerHTML = `
|
|
||||||
<div class="alert-warning">
|
|
||||||
<b>You're Offline</b>
|
<b>You're Offline</b>
|
||||||
<p>You can create an fuel record locally on your device and sync it later</p>
|
<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">Create offline record</h1>
|
<h1 class="header-form"><?= $this->get('title') ?></h1>
|
||||||
<form id="offline_refuel_add">
|
<!-- <?php if ($this->get('error')): ?> -->
|
||||||
|
<!-- <div class="error" style="color: red; margin-bottom: 1rem;"> -->
|
||||||
|
<!-- <?= htmlspecialchars($this->get('error')) ?> -->
|
||||||
|
<!-- </div> -->
|
||||||
|
<!-- <?php endif; ?> -->
|
||||||
|
<form method="POST" action="/refuel/create">
|
||||||
<label for="vehicle">Vehicle</label>
|
<label for="vehicle">Vehicle</label>
|
||||||
<select name="vehicle" id="vehicle">
|
<select name="vehicle" id="vehicle">
|
||||||
${vehicles
|
<!-- <?php foreach ($this->get('vehicles') as $vehicle): ?> -->
|
||||||
.map(
|
<!-- <option value="<?= $vehicle['id'] ?>"><?= $vehicle['name'] . " | " . $vehicle['registration_plate'] ?></option> -->
|
||||||
(el) =>
|
<!-- <?php endforeach; ?> -->
|
||||||
`<option value="${el.id}">${el.name} | ${el.registration_plate}</option>`,
|
|
||||||
)
|
|
||||||
.join("")}
|
|
||||||
</select>
|
</select>
|
||||||
|
<!-- <?php if (isset($this->get('validationErrors')['vehicle'])): ?> -->
|
||||||
|
<!-- <small class="error"><?= $this->get('validationErrors')['vehicle'] ?></small> -->
|
||||||
|
<!-- <?php endif; ?> -->
|
||||||
|
|
||||||
<label for="fuel_type">Fuel type</label>
|
<label for="fuel_type">Fuel type</label>
|
||||||
<select name="fuel_type" id="fuel_type">
|
<select name="fuel_type" id="fuel_type">
|
||||||
@ -186,88 +62,37 @@ btnOffline.addEventListener("click", async (e) => {
|
|||||||
<option value="Premium Gasoline 98">Premium Gasoline 98</option>
|
<option value="Premium Gasoline 98">Premium Gasoline 98</option>
|
||||||
<option value="Other">Other</option>
|
<option value="Other">Other</option>
|
||||||
</select>
|
</select>
|
||||||
|
<!-- <?php if (isset($this->get('validationErrors')['fuel_type'])): ?> -->
|
||||||
|
<!-- <small class="error"><?= $this->get('validationErrors')['fuel_type'] ?></small> -->
|
||||||
|
<!-- <?php endif; ?> -->
|
||||||
|
|
||||||
<label for="liters">Liters</label>
|
<label for="liters">Liters</label>
|
||||||
<input type="number" name="liters" id="liters" min="0" step=".01" value="0.0">
|
<input type="number" name="liters" id="liters" min="0" step=".01" value="<?= htmlspecialchars($_POST['liters'] ?? '0.0') ?>">
|
||||||
|
<!-- <?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; ?> -->
|
||||||
|
|
||||||
<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="0.0">
|
<input type="number" name="price_per_liter" id="price_per_liter" min="0" step=".01" value="<?= htmlspecialchars($_POST['price_per_liter'] ?? '0.0') ?>">
|
||||||
|
<!-- <?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; ?> -->
|
||||||
|
|
||||||
<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="0.0">
|
<input type="number" name="total_price" id="total_price" min="0" step=".01" value="<?= htmlspecialchars($_POST['total_price'] ?? '0.0') ?>">
|
||||||
|
<!-- <?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; ?> -->
|
||||||
<label for="mileage">Mileage</label>
|
|
||||||
<input type="number" name="mileage" id="mileage" min="0" step="1" value="0">
|
|
||||||
<!-- <small class="error"><?= $this->get('validationErrors')['mileage'] ?></small> -->
|
|
||||||
|
|
||||||
<label for="note">Note</label>
|
<label for="note">Note</label>
|
||||||
<input type="text" name="note" id="note">
|
<input type="text" name="note" id="note" value="<?= htmlspecialchars($_POST['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; ?> -->
|
||||||
|
|
||||||
<input type="submit" id="btn-offline-submit" value="Create fuel record">
|
<input type="submit" value="Create fuel record">
|
||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
`;
|
`;
|
||||||
document.querySelector("main").appendChild(offline);
|
document.querySelector("main").appendChild(offline);
|
||||||
|
|
||||||
const inp_lit = document.querySelector("input#liters");
|
|
||||||
const inp_ppl = document.querySelector("input#price_per_liter");
|
|
||||||
const inp_tot = document.querySelector("input#total_price");
|
|
||||||
|
|
||||||
const rnd = (num) => Math.round((num + Number.EPSILON) * 100) / 100;
|
|
||||||
|
|
||||||
function calculate() {
|
|
||||||
let liters = Number(inp_lit.value);
|
|
||||||
let price_per_liter = Number(inp_ppl.value);
|
|
||||||
let total_price = Number(inp_tot.value);
|
|
||||||
|
|
||||||
if (price_per_liter > 0 && liters > 0) {
|
|
||||||
total_price = rnd(liters * price_per_liter);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (price_per_liter > 0 && total_price > 0) {
|
|
||||||
liters = rnd(total_price / price_per_liter);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (liters > 0 && total_price > 0) {
|
|
||||||
price_per_liter = rnd(total_price / liters);
|
|
||||||
}
|
|
||||||
|
|
||||||
inp_lit.value = liters;
|
|
||||||
inp_ppl.value = price_per_liter;
|
|
||||||
inp_tot.value = total_price;
|
|
||||||
}
|
|
||||||
|
|
||||||
[inp_lit, inp_ppl, inp_tot].forEach((inp) => {
|
|
||||||
inp.addEventListener("change", () => {
|
|
||||||
calculate();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
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,
|
|
||||||
mileage: document.querySelector("form#offline_refuel_add #mileage").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