Compare commits
	
		
			20 Commits
		
	
	
		
			147e3b1499
			...
			fr/offline
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 18c78e37a4 | |||
| ccbb0eac64 | |||
| 21c2f4598b | |||
| 860a20d946 | |||
| c5955010cb | |||
| fc163431f8 | |||
| 15029970d6 | |||
| e13edeccfc | |||
| be6b465684 | |||
| aded859a79 | |||
| c29bd7cbab | |||
| eff5be49c4 | |||
| d13c490efb | |||
| d98c208df9 | |||
| 85af85b1ee | |||
| 1ff7fc454f | |||
| e0dfc7120f | |||
| 9c90710bf3 | |||
| 4b8ee90d8a | |||
| 59246decd7 | 
							
								
								
									
										36
									
								
								.gitea/workflows/deploy.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								.gitea/workflows/deploy.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					name: Build and Deploy Zola Website
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					on:
 | 
				
			||||||
 | 
					  push:
 | 
				
			||||||
 | 
					    branches:
 | 
				
			||||||
 | 
					      - master
 | 
				
			||||||
 | 
					env:
 | 
				
			||||||
 | 
					  HOST: ${{ secrets.SERVER_IP }}
 | 
				
			||||||
 | 
					  SSH_USERNAME: ${{ secrets.USERNAME }}
 | 
				
			||||||
 | 
					  SSH_PRIVATE_KEY: ${{ secrets.DEPLOY_KEY }}
 | 
				
			||||||
 | 
					  DEST_FOLDER: "/srv/www/cz/filiprojek/fuelstats"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jobs:
 | 
				
			||||||
 | 
					  build_and_deploy:
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					      - name: Checkout Repository
 | 
				
			||||||
 | 
					        uses: actions/checkout@v4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Deploy
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          apt update -y && apt-get install -y --no-install-recommends rsync
 | 
				
			||||||
 | 
					          eval "$(ssh-agent -s)"
 | 
				
			||||||
 | 
					          ssh-add - <<< "${SSH_PRIVATE_KEY}"
 | 
				
			||||||
 | 
					          mkdir -p ~/.ssh/
 | 
				
			||||||
 | 
					          ssh-keyscan -H ${HOST} >> ~/.ssh/known_hosts
 | 
				
			||||||
 | 
					          rsync -r --delete-after ./* "${SSH_USERNAME}@${HOST}:${{ env.DEST_FOLDER }}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Copy environment.php
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          eval "$(ssh-agent -s)"
 | 
				
			||||||
 | 
					          ssh-add - <<< "${SSH_PRIVATE_KEY}"
 | 
				
			||||||
 | 
					          mkdir -p ~/.ssh/
 | 
				
			||||||
 | 
					          ssh-keyscan -H ${HOST} >> ~/.ssh/known_hosts
 | 
				
			||||||
 | 
					          ssh ${SSH_USERNAME}@${HOST} "cp /var/websrvenv/environment.php /srv/www/cz/filiprojek/fuelstats/config/environment.php"
 | 
				
			||||||
							
								
								
									
										6
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								LICENSE
									
									
									
									
									
								
							@@ -208,8 +208,8 @@ If you develop a new program, and you want it to be of the greatest possible use
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found.
 | 
					To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
     habit-tracker
 | 
					     Fuel Stats
 | 
				
			||||||
     Copyright (C) 2024  fr
 | 
					     Copyright (C) 2024  Filip Rojek
 | 
				
			||||||
 | 
					
 | 
				
			||||||
     This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 | 
					     This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -221,7 +221,7 @@ Also add information on how to contact you by electronic and paper mail.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode:
 | 
					If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
     habit-tracker  Copyright (C) 2024  fr
 | 
					     Fuel Stats  Copyright (C) 2024  Filip Rojek
 | 
				
			||||||
     This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
 | 
					     This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
 | 
				
			||||||
     This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details.
 | 
					     This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										54
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										54
									
								
								README.md
									
									
									
									
									
								
							@@ -1,32 +1,32 @@
 | 
				
			|||||||
# Habit Tracker
 | 
					# Fuel Stats
 | 
				
			||||||
 | 
					
 | 
				
			||||||
An app for tracking habits and motivation to achieve personal goals
 | 
					An app for tracking your fuel consumption and optimizing your driving efficiency.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Used technologies
 | 
					## Used Technologies
 | 
				
			||||||
- **Frontend:** HTML, CSS, JavaScript
 | 
					- **Frontend:** HTML, CSS, JavaScript
 | 
				
			||||||
- **Backend:** PHP (OOP)
 | 
					- **Backend:** PHP (OOP)
 | 
				
			||||||
- **Database:** MariaDB
 | 
					- **Database:** MariaDB
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## How to build
 | 
					## How to Build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Build using docker
 | 
					### Build Using Docker
 | 
				
			||||||
Run the container using docker-compose
 | 
					Run the container using docker-compose:
 | 
				
			||||||
```bash
 | 
					```bash
 | 
				
			||||||
docker-compose up
 | 
					docker-compose --profile <dev|prod> up -d
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The app should be available at http://localhost:8000
 | 
					The app should be available at http://localhost:8000.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PhpMyAdmin should be available at http://localhost:8080
 | 
					PhpMyAdmin should be available at http://localhost:8080.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Build manually
 | 
					### Build Manually
 | 
				
			||||||
1. Clone the repo
 | 
					1. Clone the repository:
 | 
				
			||||||
```bash
 | 
					```bash
 | 
				
			||||||
   git clone https://git.filiprojek.cz/fr/habit-tracker.git
 | 
					   git clone https://git.filiprojek.cz/fr/fuel-stats.git
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
2. Create `config/environment.php`
 | 
					2. Create `config/environment.php`:
 | 
				
			||||||
- It should have following structure:
 | 
					- It should have the following structure:
 | 
				
			||||||
```php
 | 
					```php
 | 
				
			||||||
<?php
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -35,21 +35,23 @@ define('DB_USER', 'your db username');
 | 
				
			|||||||
define('DB_PASS', 'your db password');
 | 
					define('DB_PASS', 'your db password');
 | 
				
			||||||
define('DB_NAME', 'your db name'); 
 | 
					define('DB_NAME', 'your db name'); 
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
- For the database, you can use included `docker-compose.yaml` which have both MariaDB and PhpMyAdmin
 | 
					- For the database, you can use the included `docker-compose.yaml` which includes both MariaDB and PhpMyAdmin.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
3. Start an local web server
 | 
					3. Start a local web server:
 | 
				
			||||||
- You can use php's integrated server by running this:
 | 
					- You can use PHP's integrated server by running this:
 | 
				
			||||||
```bash
 | 
					```bash
 | 
				
			||||||
php -S localhost:8000
 | 
					php -S localhost:8000 -t ./public
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
- You can use any host and any port you want.
 | 
					- You can use any host and any port you prefer.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Usage
 | 
					## Usage
 | 
				
			||||||
1. Register and Login to the app.
 | 
					1. Register and log in to the app.
 | 
				
			||||||
2. Add your habits.
 | 
					2. Add your vehicles with their details (fuel type, registration, etc.).
 | 
				
			||||||
3. Mark your habits when you're done doing them.
 | 
					3. Record each refueling:
 | 
				
			||||||
4. Earn point and unlock achievements by completing you're habits!
 | 
					   - Select your vehicle.
 | 
				
			||||||
 | 
					   - Input the number of liters, price per liter, and total cost.
 | 
				
			||||||
## Licence
 | 
					4. Track your fuel consumption and spending through the dashboard.
 | 
				
			||||||
This project is licensed under GPL3.0 and later. More information is availabe in `LICENSE` file.
 | 
					5. View detailed stats and graphs to analyze your driving habits.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## License
 | 
				
			||||||
 | 
					This project is licensed under GPL3.0 and later. More information is available in the `LICENSE` file.
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										17
									
								
								TODO.md
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								TODO.md
									
									
									
									
									
								
							@@ -6,13 +6,10 @@
 | 
				
			|||||||
- [ ] edit user data - change password, mail...
 | 
					- [ ] edit user data - change password, mail...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Core of the app
 | 
					## Core of the app
 | 
				
			||||||
- [ ] header and navbar
 | 
					- [ ] intro tutorial when no car exist or just dont show anything
 | 
				
			||||||
- [ ] dashboard
 | 
					- [ ] change/set default car
 | 
				
			||||||
  - [x] css
 | 
					- [ ] specific car view - charts, fuel records
 | 
				
			||||||
  - [ ] its just plain
 | 
					- [ ] remove/edit fuel record
 | 
				
			||||||
  - [ ] graphs
 | 
					
 | 
				
			||||||
- [x] Habits list
 | 
					- [ ] IndexDB
 | 
				
			||||||
  - [ ] css
 | 
					  - [ ] 
 | 
				
			||||||
- [ ] Habits create
 | 
					 | 
				
			||||||
  - [ ] validate cron input
 | 
					 | 
				
			||||||
- [ ] Habits track
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,10 +25,10 @@ class AuthController extends Controller  {
 | 
				
			|||||||
            if($result === true) {
 | 
					            if($result === true) {
 | 
				
			||||||
                $this->redirect('/dashboard');
 | 
					                $this->redirect('/dashboard');
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                $this->view('auth/signin', ['error' => $result]);
 | 
					                $this->view('auth/signin', ['error' => $result], 'noheader');
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            $this->view('auth/signin', ['title' => 'Log In']);
 | 
					            $this->view('auth/signin', ['title' => 'Log In'], 'noheader');
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -54,7 +54,7 @@ class AuthController extends Controller  {
 | 
				
			|||||||
                $this->view('auth/signup', [
 | 
					                $this->view('auth/signup', [
 | 
				
			||||||
                    'error' => 'Please correct the errors below.',
 | 
					                    'error' => 'Please correct the errors below.',
 | 
				
			||||||
                    'validationErrors' => $validator->errors() ?: [],
 | 
					                    'validationErrors' => $validator->errors() ?: [],
 | 
				
			||||||
                ]);
 | 
					                ], 'noheader');
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -67,13 +67,13 @@ class AuthController extends Controller  {
 | 
				
			|||||||
                $this->view('auth/signup', [
 | 
					                $this->view('auth/signup', [
 | 
				
			||||||
                    'error' => $result,
 | 
					                    'error' => $result,
 | 
				
			||||||
                    'validationErrors' => [],
 | 
					                    'validationErrors' => [],
 | 
				
			||||||
                ]);
 | 
					                ], 'noheader');
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            $this->view('auth/signup', [
 | 
					            $this->view('auth/signup', [
 | 
				
			||||||
                'title' => 'Register',
 | 
					                'title' => 'Register',
 | 
				
			||||||
                'validationErrors' => [],
 | 
					                'validationErrors' => [],
 | 
				
			||||||
            ]);
 | 
					            ], 'noheader');
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,12 +1,38 @@
 | 
				
			|||||||
<?php
 | 
					<?php
 | 
				
			||||||
class DashboardController extends Controller {
 | 
					class DashboardController extends Controller {
 | 
				
			||||||
    public function index() {
 | 
					    public function index() {
 | 
				
			||||||
        $habit = new Habit();
 | 
					        $vehicle = new Vehicle();
 | 
				
			||||||
        $habits = $habit->getHabitsByUser($_SESSION['user']['id']);
 | 
					        $vehicles = $vehicle->getVehiclesByUser($_SESSION['user']['id']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $default_car = $vehicle->getDefaultVehicle($_SESSION['user']['id']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $refuel = new Refuel();
 | 
				
			||||||
 | 
					        $data = [
 | 
				
			||||||
 | 
					            "date" => [],
 | 
				
			||||||
 | 
					            "price" => [],
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					        $raw_data = $refuel->latest_data($default_car['id'], 5);
 | 
				
			||||||
 | 
					        foreach($raw_data as $one) {
 | 
				
			||||||
 | 
					            array_push($data['date'], date('d. m.', strtotime($one['created_at'])));
 | 
				
			||||||
 | 
					            array_push($data['price'], $one['price_per_liter']);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $latest_record = [
 | 
				
			||||||
 | 
					            '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',
 | 
				
			||||||
            'habits' => $habits,
 | 
					            'vehicles' => $vehicles,
 | 
				
			||||||
 | 
					            'date_price_data' => $data,
 | 
				
			||||||
 | 
					            'default_car' => $default_car,
 | 
				
			||||||
 | 
					            'latest_record' => $latest_record,
 | 
				
			||||||
        ]);
 | 
					        ]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,57 +0,0 @@
 | 
				
			|||||||
<?php
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class HabitController extends Controller {
 | 
					 | 
				
			||||||
    public function index() {
 | 
					 | 
				
			||||||
        $habit = new Habit();
 | 
					 | 
				
			||||||
        $habits = $habit->getHabitsByUser($_SESSION['user']['id']);
 | 
					 | 
				
			||||||
        $this->view('habits/index', ['title' => 'Habits', 'habits' => $habits]);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public function create() {
 | 
					 | 
				
			||||||
        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
 | 
					 | 
				
			||||||
            $name = $_POST['name'] ?? '';
 | 
					 | 
				
			||||||
            $frequency = $_POST['frequency'] ?? 'Daily';
 | 
					 | 
				
			||||||
            $customFrequency = null;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (empty($name)) {
 | 
					 | 
				
			||||||
                $this->view('habits/create', ['error' => 'Habit name is required.']);
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if ($frequency === 'Custom') {
 | 
					 | 
				
			||||||
                $daysOfWeek = $_POST['days_of_week'] ?? [];
 | 
					 | 
				
			||||||
                $daysOfMonth = $_POST['days_of_month'] ?? '*';
 | 
					 | 
				
			||||||
                $months = $_POST['months'] ?? '*';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // Combine into crontab-like string
 | 
					 | 
				
			||||||
                $customFrequency = implode(',', $daysOfWeek) . " $daysOfMonth $months";
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            $habit = new Habit();
 | 
					 | 
				
			||||||
            $result = $habit->create([
 | 
					 | 
				
			||||||
                'name' => $name,
 | 
					 | 
				
			||||||
                'frequency' => $frequency,
 | 
					 | 
				
			||||||
                'custom_frequency' => $customFrequency,
 | 
					 | 
				
			||||||
                'reward_points' => intval($_POST['difficulty'] ?? 1),
 | 
					 | 
				
			||||||
                'user_id' => $_SESSION['user']['id'],
 | 
					 | 
				
			||||||
            ]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if ($result) {
 | 
					 | 
				
			||||||
                $this->redirect('/habits');
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                $this->view('habits/create', ['error' => 'Failed to create habit.']);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            $this->view('habits/create', ['title' => 'Create Habit']);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public function edit() {
 | 
					 | 
				
			||||||
        // Edit habit (to be implemented later)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public function delete() {
 | 
					 | 
				
			||||||
        // Delete habit (to be implemented later)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										85
									
								
								app/controllers/RefuelController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								app/controllers/RefuelController.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,85 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class RefuelController extends Controller {
 | 
				
			||||||
 | 
					    public function create() {
 | 
				
			||||||
 | 
					        if($_SERVER['REQUEST_METHOD'] === 'GET'){
 | 
				
			||||||
 | 
					            $vehicle = new Vehicle();
 | 
				
			||||||
 | 
					            $vehicles = $vehicle->getVehiclesByUser($_SESSION['user']['id']);
 | 
				
			||||||
 | 
					            $this->view('refuel/create', [
 | 
				
			||||||
 | 
					                'title' => "New refuel record",
 | 
				
			||||||
 | 
					                'vehicles' => $vehicles,
 | 
				
			||||||
 | 
					            ]);
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
 | 
				
			||||||
 | 
					            $vehicle_id = $_POST['vehicle'] ?? '';
 | 
				
			||||||
 | 
					            $fuel_type = $_POST['fuel_type'] ?? '';
 | 
				
			||||||
 | 
					            $liters = $_POST['liters'] ?? '';
 | 
				
			||||||
 | 
					            $price_per_liter = $_POST['price_per_liter'] ?? '';
 | 
				
			||||||
 | 
					            $total_price = $_POST['total_price'] ?? '';
 | 
				
			||||||
 | 
					            $note = $_POST['note'] ?? '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $validator = new Validator();
 | 
				
			||||||
 | 
					            $validator->required('vehicle', $vehicle_id);
 | 
				
			||||||
 | 
					            $validator->required('fuel_type', $fuel_type);
 | 
				
			||||||
 | 
					            $validator->required('liters', $liters);
 | 
				
			||||||
 | 
					            $validator->required('price_per_liter', $price_per_liter);
 | 
				
			||||||
 | 
					            $validator->required('total_price', $total_price);
 | 
				
			||||||
 | 
					            $validator->number('liters', $liters);
 | 
				
			||||||
 | 
					            $validator->number('price_per_liter', $price_per_liter);
 | 
				
			||||||
 | 
					            $validator->number('total_price', $total_price);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (round($liters * $price_per_liter, 2) != $total_price) {
 | 
				
			||||||
 | 
					                $validator->setErrors(["total_price" => "Price calculation is wrong"]);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					            if($note == "") $note = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (!$validator->passes()) {
 | 
				
			||||||
 | 
					                $vehicle = new Vehicle();
 | 
				
			||||||
 | 
					                $vehicles = $vehicle->getVehiclesByUser($_SESSION['user']['id']);
 | 
				
			||||||
 | 
					                $this->view('refuel/create', [
 | 
				
			||||||
 | 
					                    'error' => 'Please correct the errors below.',
 | 
				
			||||||
 | 
					                    'validationErrors' => $validator->errors() ?: [],
 | 
				
			||||||
 | 
					                    'vehicles' => $vehicles,
 | 
				
			||||||
 | 
					                    'title' => 'New refuel record',
 | 
				
			||||||
 | 
					                ]);
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $record = new Refuel();
 | 
				
			||||||
 | 
					            $result = $record->create([
 | 
				
			||||||
 | 
					                'user_id' => $_SESSION['user']['id'],
 | 
				
			||||||
 | 
					                'vehicle_id' => $vehicle_id,
 | 
				
			||||||
 | 
					                'fuel_type' => $fuel_type,
 | 
				
			||||||
 | 
					                'note' => $note,
 | 
				
			||||||
 | 
					                'liters' => $liters,
 | 
				
			||||||
 | 
					                'price_per_liter' => $price_per_liter,
 | 
				
			||||||
 | 
					                'total_price' => $total_price,
 | 
				
			||||||
 | 
					            ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if ($result === true) {
 | 
				
			||||||
 | 
					                $this->redirect('/');
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                $vehicle = new Vehicle();
 | 
				
			||||||
 | 
					                $vehicles = $vehicle->getVehiclesByUser($_SESSION['user']['id']);
 | 
				
			||||||
 | 
					                $this->view('refuel/create', [
 | 
				
			||||||
 | 
					                    'title' => 'New refuel record',
 | 
				
			||||||
 | 
					                    'error' => $result,
 | 
				
			||||||
 | 
					                    'validationErrors' => [],
 | 
				
			||||||
 | 
					                    'vehicles' => $vehicles,
 | 
				
			||||||
 | 
					                ]);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function edit() {
 | 
				
			||||||
 | 
					        // Edit refuel record (to be implemented later)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function delete() {
 | 
				
			||||||
 | 
					        // Delete refuel record (to be implemented later)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										61
									
								
								app/controllers/VehicleController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								app/controllers/VehicleController.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,61 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class VehicleController extends Controller {
 | 
				
			||||||
 | 
					    public function index() {
 | 
				
			||||||
 | 
					        $vehicle = new Vehicle();
 | 
				
			||||||
 | 
					        $vehicles = $vehicle->getVehiclesByUser($_SESSION['user']['id']);
 | 
				
			||||||
 | 
					        $this->view('vehicles/index', ['title' => 'Vehicles', 'vehicles' => $vehicles]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function create() {
 | 
				
			||||||
 | 
					        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
 | 
				
			||||||
 | 
					            $name = $_POST['name'] ?? '';
 | 
				
			||||||
 | 
					            $registration_plate = $_POST['registration_plate'] ?? '';
 | 
				
			||||||
 | 
					            $fuel_type = $_POST['fuel_type'] ?? '';
 | 
				
			||||||
 | 
					            $note = $_POST['note'] ?? '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $validator = new Validator();
 | 
				
			||||||
 | 
					            $validator->required('name', $name);
 | 
				
			||||||
 | 
					            $validator->required('registration_plate', $registration_plate);
 | 
				
			||||||
 | 
					            $validator->required('fuel_type', $fuel_type);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if($note == "") $note = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (!$validator->passes()) {
 | 
				
			||||||
 | 
					                $this->view('vehicles/create', [
 | 
				
			||||||
 | 
					                    'error' => 'Please correct the errors below.',
 | 
				
			||||||
 | 
					                    'validationErrors' => $validator->errors() ?: [],
 | 
				
			||||||
 | 
					                ]);
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $vehicle = new Vehicle();
 | 
				
			||||||
 | 
					            $result = $vehicle->create([
 | 
				
			||||||
 | 
					                'name' => $name,
 | 
				
			||||||
 | 
					                'registration_plate' => strtoupper($registration_plate),
 | 
				
			||||||
 | 
					                'fuel_type' => $fuel_type,
 | 
				
			||||||
 | 
					                'note' => $note,
 | 
				
			||||||
 | 
					                'user_id' => $_SESSION['user']['id'],
 | 
				
			||||||
 | 
					            ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if ($result === true) {
 | 
				
			||||||
 | 
					                $this->redirect('/vehicles');
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                $this->view('vehicles/create', ['title' => 'Create vehicle', 'error' => $result, 'validationErrors' => []] );
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            $this->view('vehicles/create', ['title' => 'Create Vehicle']);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function edit() {
 | 
				
			||||||
 | 
					        // Edit vehicle (to be implemented later)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function delete() {
 | 
				
			||||||
 | 
					        // Delete vehicle (to be implemented later)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,46 +0,0 @@
 | 
				
			|||||||
<?php
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class Habit {
 | 
					 | 
				
			||||||
    private $db;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public function __construct() {
 | 
					 | 
				
			||||||
        $this->db = Database::getInstance()->getConnection();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public function create($data) {
 | 
					 | 
				
			||||||
        $stmt = $this->db->prepare("
 | 
					 | 
				
			||||||
            INSERT INTO habits (user_id, title, frequency, custom_frequency, reward_points, created_at)
 | 
					 | 
				
			||||||
            VALUES (?, ?, ?, ?, ?, NOW())
 | 
					 | 
				
			||||||
        ");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        $stmt->bind_param(
 | 
					 | 
				
			||||||
            "isssi", // Bind types: int, string, string, string, int
 | 
					 | 
				
			||||||
            $data['user_id'],
 | 
					 | 
				
			||||||
            $data['name'],
 | 
					 | 
				
			||||||
            $data['frequency'],
 | 
					 | 
				
			||||||
            $data['custom_frequency'], // Bind the custom_frequency field
 | 
					 | 
				
			||||||
            $data['reward_points']
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if ($stmt->execute()) {
 | 
					 | 
				
			||||||
            return true;
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            error_log("Failed to create habit: " . $stmt->error);
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public function getHabitsByUser($userId) {
 | 
					 | 
				
			||||||
        $stmt = $this->db->prepare("SELECT id, title, frequency, custom_frequency, reward_points, created_at FROM habits WHERE user_id = ?");
 | 
					 | 
				
			||||||
        $stmt->bind_param("i", $userId);
 | 
					 | 
				
			||||||
        $stmt->execute();
 | 
					 | 
				
			||||||
        $result = $stmt->get_result();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        $habits = [];
 | 
					 | 
				
			||||||
        while ($row = $result->fetch_assoc()) {
 | 
					 | 
				
			||||||
            $habits[] = $row;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        return $habits;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										92
									
								
								app/models/Refuel.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								app/models/Refuel.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,92 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Refuel {
 | 
				
			||||||
 | 
					    private $db;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function __construct() {
 | 
				
			||||||
 | 
					        $this->db = Database::getInstance()->getConnection();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function create($data) {
 | 
				
			||||||
 | 
					        try{
 | 
				
			||||||
 | 
					            $stmt = $this->db->prepare("
 | 
				
			||||||
 | 
					                INSERT INTO refueling_records (user_id, vehicle_id, fuel_type, note, liters, price_per_liter, total_price, created_at)
 | 
				
			||||||
 | 
					                VALUES (?, ?, ?, ?, ?, ?, ?, NOW())
 | 
				
			||||||
 | 
					            ");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $stmt->bind_param(
 | 
				
			||||||
 | 
					                "iissddd",
 | 
				
			||||||
 | 
					                $data['user_id'],
 | 
				
			||||||
 | 
					                $data['vehicle_id'],
 | 
				
			||||||
 | 
					                $data['fuel_type'],
 | 
				
			||||||
 | 
					                $data['note'],
 | 
				
			||||||
 | 
					                $data['liters'],
 | 
				
			||||||
 | 
					                $data['price_per_liter'],
 | 
				
			||||||
 | 
					                $data['total_price'],
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if ($stmt->execute()) {
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                return "Error: " . $stmt->error;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch(mysqli_sql_exception $e) {
 | 
				
			||||||
 | 
					            return $e->getMessage();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function latest_data($vehicle_id, $record_count) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            $stmt = $this->db->prepare("
 | 
				
			||||||
 | 
					                SELECT `liters`, `price_per_liter`, `total_price`, `created_at`
 | 
				
			||||||
 | 
					                FROM `refueling_records`
 | 
				
			||||||
 | 
					                WHERE `vehicle_id` = ?
 | 
				
			||||||
 | 
					                ORDER BY created_at DESC
 | 
				
			||||||
 | 
					                LIMIT ?;
 | 
				
			||||||
 | 
					            ");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $stmt->bind_param("ii", $vehicle_id, $record_count);
 | 
				
			||||||
 | 
					            if ($stmt->execute()) {
 | 
				
			||||||
 | 
					                $result = $stmt->get_result();
 | 
				
			||||||
 | 
					                $data = $result->fetch_all(MYSQLI_ASSOC);
 | 
				
			||||||
 | 
					                $stmt->close();
 | 
				
			||||||
 | 
					                return array_reverse($data);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                return "Error: " . $stmt->error;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch (mysqli_sql_exception $e) {
 | 
				
			||||||
 | 
					            return $e->getMessage();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function latest_one($user_id, $record_count = 1) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            $stmt = $this->db->prepare("
 | 
				
			||||||
 | 
					                SELECT 
 | 
				
			||||||
 | 
					                    `r`.`vehicle_id`, 
 | 
				
			||||||
 | 
					                    `v`.`name` AS `vehicle_name`, 
 | 
				
			||||||
 | 
					                    `r`.`liters`, 
 | 
				
			||||||
 | 
					                    `r`.`price_per_liter`, 
 | 
				
			||||||
 | 
					                    `r`.`total_price`, 
 | 
				
			||||||
 | 
					                    `r`.`created_at`
 | 
				
			||||||
 | 
					                FROM `refueling_records` AS `r`
 | 
				
			||||||
 | 
					                JOIN `vehicles` AS `v` ON `r`.`vehicle_id` = `v`.`id`
 | 
				
			||||||
 | 
					                WHERE `r`.`user_id` = ?
 | 
				
			||||||
 | 
					                ORDER BY `r`.`created_at` DESC
 | 
				
			||||||
 | 
					                LIMIT ?;
 | 
				
			||||||
 | 
					            ");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $stmt->bind_param("ii", $user_id, $record_count);
 | 
				
			||||||
 | 
					            if ($stmt->execute()) {
 | 
				
			||||||
 | 
					                $result = $stmt->get_result();
 | 
				
			||||||
 | 
					                $data = $result->fetch_all(MYSQLI_ASSOC);
 | 
				
			||||||
 | 
					                $stmt->close();
 | 
				
			||||||
 | 
					                return array_reverse($data);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                return "Error: " . $stmt->error;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch (mysqli_sql_exception $e) {
 | 
				
			||||||
 | 
					            return $e->getMessage();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										63
									
								
								app/models/Vehicle.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								app/models/Vehicle.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Vehicle {
 | 
				
			||||||
 | 
					    private $db;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function __construct() {
 | 
				
			||||||
 | 
					        $this->db = Database::getInstance()->getConnection();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function create($data) {
 | 
				
			||||||
 | 
					        try{
 | 
				
			||||||
 | 
					            $stmt = $this->db->prepare("
 | 
				
			||||||
 | 
					                INSERT INTO vehicles (user_id, name, registration_plate, fuel_type, note, created_at)
 | 
				
			||||||
 | 
					                VALUES (?, ?, ?, ?, ?, NOW())
 | 
				
			||||||
 | 
					            ");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $stmt->bind_param(
 | 
				
			||||||
 | 
					                "issss",
 | 
				
			||||||
 | 
					                $data['user_id'],
 | 
				
			||||||
 | 
					                $data['name'],
 | 
				
			||||||
 | 
					                $data['registration_plate'],
 | 
				
			||||||
 | 
					                $data['fuel_type'],
 | 
				
			||||||
 | 
					                $data['note'],
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if ($stmt->execute()) {
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                return "Error: " . $stmt->error;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch(mysqli_sql_exception $e) {
 | 
				
			||||||
 | 
					            return $e->getMessage();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function getVehiclesByUser($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->execute();
 | 
				
			||||||
 | 
					        $result = $stmt->get_result();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $vehicles = [];
 | 
				
			||||||
 | 
					        while ($row = $result->fetch_assoc()) {
 | 
				
			||||||
 | 
					            $vehicles[] = $row;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        return $vehicles;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function getDefaultVehicle($user_id) {
 | 
				
			||||||
 | 
					        $stmt = $this->db->prepare("
 | 
				
			||||||
 | 
					            SELECT id, name, registration_plate, fuel_type, note, is_default
 | 
				
			||||||
 | 
					            FROM vehicles
 | 
				
			||||||
 | 
					            WHERE user_id = ? AND is_default = TRUE
 | 
				
			||||||
 | 
					            LIMIT 1
 | 
				
			||||||
 | 
					        ");
 | 
				
			||||||
 | 
					        $stmt->bind_param("i", $user_id);
 | 
				
			||||||
 | 
					        $stmt->execute();
 | 
				
			||||||
 | 
					        $result = $stmt->get_result();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $result->fetch_assoc();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -2,8 +2,8 @@
 | 
				
			|||||||
<link rel="stylesheet" href="/css/form.css">
 | 
					<link rel="stylesheet" href="/css/form.css">
 | 
				
			||||||
<section class="form signin">
 | 
					<section class="form signin">
 | 
				
			||||||
    <div class="header-form">
 | 
					    <div class="header-form">
 | 
				
			||||||
        <img src="/img/logo.jpg" alt="Habit Tracker Logo">
 | 
					        <img src="/img/logo.jpg" alt="Fuel Stats Logo">
 | 
				
			||||||
        <h1>Sign in to Habit Tracker</h1>
 | 
					        <h1>Sign in to Fuel Stats</h1>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <?php if ($this->get('error')): ?>
 | 
					    <?php if ($this->get('error')): ?>
 | 
				
			||||||
@@ -25,4 +25,9 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        <input type="submit" value="Sign In">
 | 
					        <input type="submit" value="Sign In">
 | 
				
			||||||
    </form>
 | 
					    </form>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <div class="bordered">
 | 
				
			||||||
 | 
					        <p>New to Fuel Stats?</p>
 | 
				
			||||||
 | 
					        <a href="/auth/signup">Create an account</a>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
</section>
 | 
					</section>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,8 +2,8 @@
 | 
				
			|||||||
<link rel="stylesheet" href="/css/form.css">
 | 
					<link rel="stylesheet" href="/css/form.css">
 | 
				
			||||||
<section class="form signup">
 | 
					<section class="form signup">
 | 
				
			||||||
    <div class="header-form">
 | 
					    <div class="header-form">
 | 
				
			||||||
        <img src="/img/logo.jpg" alt="Habit Tracker Logo">
 | 
					        <img src="/img/logo.jpg" alt="Fuel Stats Logo">
 | 
				
			||||||
        <h1>Sign up to Habit Tracker</h1>
 | 
					        <h1>Sign up to Fuel Stats</h1>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <?php if ($this->get('error')): ?>
 | 
					    <?php if ($this->get('error')): ?>
 | 
				
			||||||
@@ -37,4 +37,9 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        <input type="submit" value="Sign Up">
 | 
					        <input type="submit" value="Sign Up">
 | 
				
			||||||
    </form>
 | 
					    </form>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <div class="bordered">
 | 
				
			||||||
 | 
					        <p>Already have an account?</p>
 | 
				
			||||||
 | 
					        <a href="/auth/signin">Sign in</a>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
</section>
 | 
					</section>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,43 +2,63 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
<section class="dashboard">
 | 
					<section class="dashboard">
 | 
				
			||||||
    <h1>Welcome, <?= htmlspecialchars($_SESSION['user']['username']) ?>!</h1>
 | 
					    <h1>Welcome, <?= htmlspecialchars($_SESSION['user']['username']) ?>!</h1>
 | 
				
			||||||
    <a href="/habits/create" class="btn-primary">Create new habit!</a>
 | 
					    <div>
 | 
				
			||||||
 | 
					        <a href="/refuel/create" class="btn-green">Add new refuel record!</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>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
    <div class="card-wrapper">
 | 
					    <div class="card-wrapper">
 | 
				
			||||||
        <section class="card upcoming">
 | 
					        <section class="card latest">
 | 
				
			||||||
            <h2>Upcoming</h2>
 | 
					            <h2>Latest fuel record</h2>
 | 
				
			||||||
            <div class="habit">
 | 
					            <hr>
 | 
				
			||||||
                <b>Habit Title</b>
 | 
					            <div>
 | 
				
			||||||
                <p>Frequency</p>
 | 
					                <b>Car:</b>
 | 
				
			||||||
                <p>Reward points</p>
 | 
					                <p><?= $data['latest_record']['vehicle_name'] ?></p>
 | 
				
			||||||
 | 
					                <b>Liters:</b>
 | 
				
			||||||
 | 
					                <p><?= $data['latest_record']['liters'] ?> liters</p>
 | 
				
			||||||
 | 
					                <b>Price per liter:</b>
 | 
				
			||||||
 | 
					                <p><?= $data['latest_record']['price_per_liter'] ?>,-/liter</p>
 | 
				
			||||||
 | 
					                <b>Total price:</b>
 | 
				
			||||||
 | 
					                <p><?= $data['latest_record']['total_price'] ?>,-</p>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
        </section>
 | 
					        </section>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <section class="card recent">
 | 
					        <section class="card">
 | 
				
			||||||
            <h2>Recent</h2>
 | 
					            <h2>Default car</h2>
 | 
				
			||||||
            <div class="habit">
 | 
					            <hr>
 | 
				
			||||||
                <b>Habit Title</b>
 | 
					            <b>Car</b>
 | 
				
			||||||
                <p>Frequency</p>
 | 
					            <p><?= $data['default_car']['name'] ?></p>
 | 
				
			||||||
                <p>Reward points</p>
 | 
					            <b>Registration plate</b>
 | 
				
			||||||
            </div>
 | 
					            <p><?= $data['default_car']['registration_plate'] ?></p>
 | 
				
			||||||
        </section>
 | 
					            <b>Fuel type</b>
 | 
				
			||||||
 | 
					            <p><?= $data['default_car']['fuel_type'] ?></p>
 | 
				
			||||||
        <section class="card missed">
 | 
					            <b>Note</b>
 | 
				
			||||||
            <h2>Missed</h2>
 | 
					            <p><?= $data['default_car']['note'] ?></p>
 | 
				
			||||||
            <div class="habit">
 | 
					 | 
				
			||||||
                <b>Habit Title</b>
 | 
					 | 
				
			||||||
                <p>Frequency</p>
 | 
					 | 
				
			||||||
                <p>Reward points</p>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
        </section>
 | 
					        </section>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <section class="card history-graph">
 | 
					        <section class="card history-graph">
 | 
				
			||||||
            <h2>Graph of History</h2>
 | 
					            <h2>Chart of Gas price</h2>
 | 
				
			||||||
        </section>
 | 
					            <hr>
 | 
				
			||||||
 | 
					            <p><?= $data['default_car']['name'] . " | " . $data['default_car']['registration_plate']?></p>
 | 
				
			||||||
        <section class="card streak">
 | 
					            <canvas id="chart-gas-price"></canvas>
 | 
				
			||||||
            <h2>Streak</h2>
 | 
					 | 
				
			||||||
            <p>You're current streak is 123 days</p>
 | 
					 | 
				
			||||||
            <p>Good job!</p>
 | 
					 | 
				
			||||||
        </section>
 | 
					        </section>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
</section>
 | 
					</section>
 | 
				
			||||||
 | 
					<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
 | 
				
			||||||
 | 
					<script>
 | 
				
			||||||
 | 
					    const ctx = document.getElementById('chart-gas-price');
 | 
				
			||||||
 | 
					    const data = <?= json_encode($data['date_price_data']); ?>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    new Chart(ctx, {
 | 
				
			||||||
 | 
					        type: 'line',
 | 
				
			||||||
 | 
					        data: {
 | 
				
			||||||
 | 
					            labels: [...data['date']],
 | 
				
			||||||
 | 
					            datasets: [{
 | 
				
			||||||
 | 
					                label: 'Gas price history',
 | 
				
			||||||
 | 
					                data: data['price'],
 | 
				
			||||||
 | 
					                borderWidth: 1,
 | 
				
			||||||
 | 
					            }]
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					<script defer src="/js/offline-records.js"></script>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,7 @@
 | 
				
			|||||||
<head>
 | 
					<head>
 | 
				
			||||||
    <meta charset="UTF-8">
 | 
					    <meta charset="UTF-8">
 | 
				
			||||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
					    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
				
			||||||
    <title>Habit Tracker | Error 494</title>
 | 
					    <title>Fuel Stats | Error 494</title>
 | 
				
			||||||
</head>
 | 
					</head>
 | 
				
			||||||
<body>
 | 
					<body>
 | 
				
			||||||
    <h1>Error 404 - Page not found</h1>
 | 
					    <h1>Error 404 - Page not found</h1>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,70 +0,0 @@
 | 
				
			|||||||
<link rel="stylesheet" href="/css/form.css">
 | 
					 | 
				
			||||||
<link rel="stylesheet" href="/css/habits_create.css">
 | 
					 | 
				
			||||||
<section class="form habit-create">
 | 
					 | 
				
			||||||
    <h1 class="header-form"><?= $this->get('title', 'Create Habit') ?></h1>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    <?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="/habits/create">
 | 
					 | 
				
			||||||
        <label for="name">Habit Name:</label>
 | 
					 | 
				
			||||||
        <input type="text" name="name" id="name" required value="<?= htmlspecialchars($_POST['name'] ?? '') ?>">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <label for="frequency">Frequency:</label>
 | 
					 | 
				
			||||||
        <select name="frequency" id="frequency" onchange="toggleCustomFrequency(this.value)">
 | 
					 | 
				
			||||||
            <option value="Daily">Daily</option>
 | 
					 | 
				
			||||||
            <option value="Weekly">Weekly</option>
 | 
					 | 
				
			||||||
            <option value="Custom">Custom</option>
 | 
					 | 
				
			||||||
        </select>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <div id="custom-frequency" style="display: none;">
 | 
					 | 
				
			||||||
            <label id="lbl_dow">Days of the Week:</label>
 | 
					 | 
				
			||||||
            <div class="dow_chb_wrapper">
 | 
					 | 
				
			||||||
                <label for="dow_mon">Monday</label>
 | 
					 | 
				
			||||||
                <input type="checkbox" name="days_of_week[]" id="dow_mon" value="1">
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
            <div class="dow_chb_wrapper">
 | 
					 | 
				
			||||||
                <label for="dow_tue">Tuesday</label>
 | 
					 | 
				
			||||||
                <input type="checkbox" name="days_of_week[]" id="dow_tue" value="2">
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
            <div class="dow_chb_wrapper">
 | 
					 | 
				
			||||||
                <label for="dow_wed">Wednesday</label>
 | 
					 | 
				
			||||||
                <input type="checkbox" name="days_of_week[]" id="dow_wed" value="3">
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
            <div class="dow_chb_wrapper">
 | 
					 | 
				
			||||||
                <label for="dow_thu">Thursday</label>
 | 
					 | 
				
			||||||
                <input type="checkbox" name="days_of_week[]" id="dow_thu" value="4">
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
            <div class="dow_chb_wrapper">
 | 
					 | 
				
			||||||
                <label for="dow_fri">Friday</label>
 | 
					 | 
				
			||||||
                <input type="checkbox" name="days_of_week[]" id="dow_fri" value="5">
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
            <div class="dow_chb_wrapper">
 | 
					 | 
				
			||||||
                <label for="dow_sat">Saturday</label>
 | 
					 | 
				
			||||||
                <input type="checkbox" name="days_of_week[]" id="dow_sat" value="6">
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
            <div class="dow_chb_wrapper">
 | 
					 | 
				
			||||||
                <label for="dow_sun">Sunday</label>
 | 
					 | 
				
			||||||
                <input type="checkbox" name="days_of_week[]" id="dow_sun" value="7">
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <label for="days_of_month" id="lbl_dom">Days of the Month:</label>
 | 
					 | 
				
			||||||
            <input type="text" name="days_of_month" id="days_of_month" placeholder="1,15 (comma-separated)">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <label for="months">Months:</label>
 | 
					 | 
				
			||||||
            <input type="text" name="months" id="months" placeholder="1,7,12 (comma-separated)">
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <input type="submit" value="Create Habit">
 | 
					 | 
				
			||||||
    </form>
 | 
					 | 
				
			||||||
</section>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<script>
 | 
					 | 
				
			||||||
function toggleCustomFrequency(value) {
 | 
					 | 
				
			||||||
    const customFrequencyDiv = document.getElementById('custom-frequency');
 | 
					 | 
				
			||||||
    customFrequencyDiv.style.display = value === 'Custom' ? 'flex' : 'none';
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
@@ -1,35 +0,0 @@
 | 
				
			|||||||
<link rel="stylesheet" href="/css/habits_dashboard.css">
 | 
					 | 
				
			||||||
<section class="habits">
 | 
					 | 
				
			||||||
    <?php if (empty($this->get('habits'))): ?>
 | 
					 | 
				
			||||||
        <p>No habits yet. <a href="/habits/create">Create your first habit</a>.</p>
 | 
					 | 
				
			||||||
    <?php else: ?>
 | 
					 | 
				
			||||||
        <table>
 | 
					 | 
				
			||||||
            <thead>
 | 
					 | 
				
			||||||
                <tr>
 | 
					 | 
				
			||||||
                    <th>Title</th>
 | 
					 | 
				
			||||||
                    <th>Frequency</th>
 | 
					 | 
				
			||||||
                    <th>Custom Schedule</th>
 | 
					 | 
				
			||||||
                    <th>Points</th>
 | 
					 | 
				
			||||||
                    <th>Created At</th>
 | 
					 | 
				
			||||||
                    <th>Actions</th>
 | 
					 | 
				
			||||||
                </tr>
 | 
					 | 
				
			||||||
            </thead>
 | 
					 | 
				
			||||||
            <tbody>
 | 
					 | 
				
			||||||
                <?php foreach ($this->get('habits') as $habit): ?>
 | 
					 | 
				
			||||||
                    <tr>
 | 
					 | 
				
			||||||
                        <td><?= htmlspecialchars($habit['title']) ?></td>
 | 
					 | 
				
			||||||
                        <td><?= htmlspecialchars($habit['frequency']) ?></td>
 | 
					 | 
				
			||||||
                        <td><?= htmlspecialchars($habit['custom_frequency'] ?? 'N/A') ?></td>
 | 
					 | 
				
			||||||
                        <td><?= htmlspecialchars($habit['reward_points']) ?></td>
 | 
					 | 
				
			||||||
                        <td><?= htmlspecialchars($habit['created_at']) ?></td>
 | 
					 | 
				
			||||||
                        <td>
 | 
					 | 
				
			||||||
                            <a href="/habits/edit?id=<?= $habit['id'] ?>">Edit</a> |
 | 
					 | 
				
			||||||
                            <a href="/habits/delete?id=<?= $habit['id'] ?>" onclick="return confirm('Are you sure you want to delete this habit?')">Delete</a>
 | 
					 | 
				
			||||||
                        </td>
 | 
					 | 
				
			||||||
                    </tr>
 | 
					 | 
				
			||||||
                <?php endforeach; ?>
 | 
					 | 
				
			||||||
            </tbody>
 | 
					 | 
				
			||||||
        </table>
 | 
					 | 
				
			||||||
        <a href="/habits/create">Create new habit!</a>
 | 
					 | 
				
			||||||
    <?php endif; ?>
 | 
					 | 
				
			||||||
</section>
 | 
					 | 
				
			||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
<div>
 | 
					<div>
 | 
				
			||||||
    <h1>Welcome to Habit Tracker!</h1>
 | 
					    <h1>Welcome to Fuel Stats!</h1>
 | 
				
			||||||
    <p>Track your habits and achieve your goals.</p>
 | 
					    <p>Keep track of your refuels.</p>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,17 +6,27 @@
 | 
				
			|||||||
    <meta name="author" content="Filip Rojek | http://filiprojek.cz">
 | 
					    <meta name="author" content="Filip Rojek | http://filiprojek.cz">
 | 
				
			||||||
    <meta name="email" content="webmaster(@)fofrweb.com">
 | 
					    <meta name="email" content="webmaster(@)fofrweb.com">
 | 
				
			||||||
    <meta name="copyright" content="(c) filiprojek.cz">
 | 
					    <meta name="copyright" content="(c) filiprojek.cz">
 | 
				
			||||||
    <title>Habit Tracker | <?= $data['title'] ?></title>
 | 
					    <title>Fuel Stats<?= isset($data['title']) ? " | " . $data['title'] : "" ?></title>
 | 
				
			||||||
    <link rel="stylesheet" href="/css/main.css">
 | 
					    <link rel="stylesheet" href="/css/main.css">
 | 
				
			||||||
    <link rel="stylesheet" href="/css/global.css">
 | 
					    <link rel="stylesheet" href="/css/global.css">
 | 
				
			||||||
    <link rel="stylesheet" href="/css/vars.css">
 | 
					    <link rel="stylesheet" href="/css/vars.css">
 | 
				
			||||||
 | 
					    <link rel="stylesheet" href="/css/header.css">
 | 
				
			||||||
    <link rel="icon" type="image/x-icon" href="/img/favicon.ico">
 | 
					    <link rel="icon" type="image/x-icon" href="/img/favicon.ico">
 | 
				
			||||||
  </head>
 | 
					  </head>
 | 
				
			||||||
  <body>
 | 
					  <body>
 | 
				
			||||||
    <header>
 | 
					    <header>
 | 
				
			||||||
      <a href="/auth/signin">Log In</a>
 | 
					      <div id="hd-left">
 | 
				
			||||||
      <a href="/auth/signup">Sign Up</a>
 | 
					        <a href="/"><img src="/img/logo.jpg" alt="home"></a>
 | 
				
			||||||
      <a href="/auth/logout">Log Out</a>
 | 
					        <label><?= isset($data['title']) ? $data['title'] : "" ?></label>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <div id="hd-right">
 | 
				
			||||||
 | 
					        <?php if (!isset($_SESSION['user'])): ?>
 | 
				
			||||||
 | 
					        <a href="/auth/signin">Log In</a>
 | 
				
			||||||
 | 
					        <a href="/auth/signup">Sign Up</a>
 | 
				
			||||||
 | 
					        <?php else: ?>
 | 
				
			||||||
 | 
					        <a href="/auth/logout" class="btn-secondary">Sign out</a>
 | 
				
			||||||
 | 
					        <?php endif; ?>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
    </header>
 | 
					    </header>
 | 
				
			||||||
    <main class="content">
 | 
					    <main class="content">
 | 
				
			||||||
      <?= $content ?>
 | 
					      <?= $content ?>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										20
									
								
								app/views/layouts/noheader.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								app/views/layouts/noheader.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					<!doctype html>
 | 
				
			||||||
 | 
					<html>
 | 
				
			||||||
 | 
					  <head>
 | 
				
			||||||
 | 
					    <meta charset="UTF-8">
 | 
				
			||||||
 | 
					    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
				
			||||||
 | 
					    <meta name="author" content="Filip Rojek | http://filiprojek.cz">
 | 
				
			||||||
 | 
					    <meta name="email" content="webmaster(@)fofrweb.com">
 | 
				
			||||||
 | 
					    <meta name="copyright" content="(c) filiprojek.cz">
 | 
				
			||||||
 | 
					    <title>Fuel Stats<?= isset($data['title']) ? " | " . $data['title'] : "" ?></title>
 | 
				
			||||||
 | 
					    <link rel="stylesheet" href="/css/main.css">
 | 
				
			||||||
 | 
					    <link rel="stylesheet" href="/css/global.css">
 | 
				
			||||||
 | 
					    <link rel="stylesheet" href="/css/vars.css">
 | 
				
			||||||
 | 
					    <link rel="icon" type="image/x-icon" href="/img/favicon.ico">
 | 
				
			||||||
 | 
					  </head>
 | 
				
			||||||
 | 
					  <body>
 | 
				
			||||||
 | 
					    <main class="content">
 | 
				
			||||||
 | 
					      <?= $content ?>
 | 
				
			||||||
 | 
					    </main>
 | 
				
			||||||
 | 
					  </body>
 | 
				
			||||||
 | 
					</html>
 | 
				
			||||||
							
								
								
									
										119
									
								
								app/views/refuel/create.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								app/views/refuel/create.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,119 @@
 | 
				
			|||||||
 | 
					<link rel="stylesheet" href="/css/form.css">
 | 
				
			||||||
 | 
					<link rel="stylesheet" href="/css/vehicle_create.css">
 | 
				
			||||||
 | 
					<section class="form">
 | 
				
			||||||
 | 
					    <h1 class="header-form"><?= $this->get('title') ?></h1>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <?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>
 | 
				
			||||||
 | 
					        <select name="vehicle" id="vehicle">
 | 
				
			||||||
 | 
					        <?php foreach ($this->get('vehicles') as $vehicle): ?>
 | 
				
			||||||
 | 
					            <option value="<?= $vehicle['id'] ?>"><?= $vehicle['name'] . " | " . $vehicle['registration_plate'] ?></option>
 | 
				
			||||||
 | 
					        <?php endforeach; ?>
 | 
				
			||||||
 | 
					        </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>
 | 
				
			||||||
 | 
					        <select name="fuel_type" id="fuel_type">
 | 
				
			||||||
 | 
					            <option value="Diesel">Diesel</option>
 | 
				
			||||||
 | 
					            <option value="Gasoline 95">Gasoline 95</option>
 | 
				
			||||||
 | 
					            <option value="Gasoline 98">Gasoline 98</option>
 | 
				
			||||||
 | 
					            <option value="Premium Diesel">Premium Diesel</option>
 | 
				
			||||||
 | 
					            <option value="Premium Gasoline 95">Premium Gasoline 95</option>
 | 
				
			||||||
 | 
					            <option value="Premium Gasoline 98">Premium Gasoline 98</option>
 | 
				
			||||||
 | 
					            <option value="Other">Other</option>
 | 
				
			||||||
 | 
					        </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>
 | 
				
			||||||
 | 
					        <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>
 | 
				
			||||||
 | 
					        <?php endif; ?>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <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') ?>">
 | 
				
			||||||
 | 
					        <?php if (isset($this->get('validationErrors')['price_per_liter'])): ?>
 | 
				
			||||||
 | 
					            <small class="error"><?= $this->get('validationErrors')['price_per_liter'] ?></small>
 | 
				
			||||||
 | 
					        <?php endif; ?>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <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') ?>">
 | 
				
			||||||
 | 
					        <?php if (isset($this->get('validationErrors')['total_price'])): ?>
 | 
				
			||||||
 | 
					            <small class="error"><?= $this->get('validationErrors')['total_price'] ?></small>
 | 
				
			||||||
 | 
					        <?php endif; ?>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <label for="note">Note</label>
 | 
				
			||||||
 | 
					        <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>
 | 
				
			||||||
 | 
					        <?php endif; ?>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <input type="submit" value="Create fuel record">
 | 
				
			||||||
 | 
					    </form>
 | 
				
			||||||
 | 
					</section>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script>
 | 
				
			||||||
 | 
					    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 vehicles = <?= json_encode($data['vehicles']); ?>;
 | 
				
			||||||
 | 
					    const fuel_sel = document.querySelector("#fuel_type")
 | 
				
			||||||
 | 
					    const vehic_sel = document.querySelector("#vehicle")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function selectFuel() {
 | 
				
			||||||
 | 
					        const veh_id = vehic_sel.value
 | 
				
			||||||
 | 
					        vehicles.forEach(el => {
 | 
				
			||||||
 | 
					            if(el.id == veh_id) {
 | 
				
			||||||
 | 
					                fuel_sel.value = el.fuel_type
 | 
				
			||||||
 | 
					                return
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    selectFuel()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    vehic_sel.addEventListener("change", () => {
 | 
				
			||||||
 | 
					        selectFuel()
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
							
								
								
									
										35
									
								
								app/views/vehicles/create.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								app/views/vehicles/create.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					<link rel="stylesheet" href="/css/form.css">
 | 
				
			||||||
 | 
					<link rel="stylesheet" href="/css/vehicle_create.css">
 | 
				
			||||||
 | 
					<section class="form">
 | 
				
			||||||
 | 
					    <h1 class="header-form"><?= $this->get('title', 'Create Vehicle') ?></h1>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <?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="/vehicles/create">
 | 
				
			||||||
 | 
					        <label for="name">Vehicle name</label>
 | 
				
			||||||
 | 
					        <input type="text" name="name" id="name" required value="<?= htmlspecialchars($_POST['name'] ?? '') ?>">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <label for="registration_plate">Registration plate</label>
 | 
				
			||||||
 | 
					        <input type="text" name="registration_plate" id="registration_plate" maxlength="10" onkeypress="return event.charCode != 32" required value="<?= htmlspecialchars($_POST['registration_plate'] ?? '') ?>">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <label for="fuel_type">Fuel type</label>
 | 
				
			||||||
 | 
					        <select name="fuel_type" id="fuel_type">
 | 
				
			||||||
 | 
					            <option value="Diesel">Diesel</option>
 | 
				
			||||||
 | 
					            <option value="Gasoline 95">Gasoline 95</option>
 | 
				
			||||||
 | 
					            <option value="Gasoline 98">Gasoline 98</option>
 | 
				
			||||||
 | 
					            <option value="Premium Diesel">Premium Diesel</option>
 | 
				
			||||||
 | 
					            <option value="Premium Gasoline 95">Premium Gasoline 95</option>
 | 
				
			||||||
 | 
					            <option value="Premium Gasoline 98">Premium Gasoline 98</option>
 | 
				
			||||||
 | 
					            <option value="Other">Other</option>
 | 
				
			||||||
 | 
					        </select>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <label for="note">Note</label>
 | 
				
			||||||
 | 
					        <input type="text" name="note" id="note" value="<?= htmlspecialchars($_POST['note'] ?? '') ?>">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <input type="submit" value="Create vehicle">
 | 
				
			||||||
 | 
					    </form>
 | 
				
			||||||
 | 
					</section>
 | 
				
			||||||
							
								
								
									
										24
									
								
								app/views/vehicles/index.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								app/views/vehicles/index.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
				
			|||||||
 | 
					<link rel="stylesheet" href="/css/vehicles.css">
 | 
				
			||||||
 | 
					<section class="vehicles">
 | 
				
			||||||
 | 
					    <?php if (empty($this->get('vehicles'))): ?>
 | 
				
			||||||
 | 
					        <p>No vehicles yet. <a href="/vehicles/create">Add your first vehicle</a>.</p>
 | 
				
			||||||
 | 
					    <?php else: ?>
 | 
				
			||||||
 | 
					        <div class="btn-wrapper">
 | 
				
			||||||
 | 
					            <a href="/vehicles/create" class="btn-green">Add new vehicle!</a>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div class="vehicle-wrapper">
 | 
				
			||||||
 | 
					        <?php foreach ($this->get('vehicles') as $vehicle): ?>
 | 
				
			||||||
 | 
					            <div class="vehicle bordered">
 | 
				
			||||||
 | 
					                <b><?= htmlspecialchars($vehicle['name']) ?></b>
 | 
				
			||||||
 | 
					                <p><?= htmlspecialchars($vehicle['registration_plate']) ?></p>
 | 
				
			||||||
 | 
					                <p><?= htmlspecialchars($vehicle['fuel_type']) ?></p>
 | 
				
			||||||
 | 
					                <p><?= htmlspecialchars($vehicle['note'] ?? "") ?></p>
 | 
				
			||||||
 | 
					                <div class="actions">
 | 
				
			||||||
 | 
					                    <a href="/vehicles/edit?id=<?= $vehicle['id'] ?>">Edit</a>
 | 
				
			||||||
 | 
					                    <a href="/vehicles/delete?id=<?= $vehicle['id'] ?>" onclick="return confirm('Are you sure you want to delete this habit?')">Delete</a>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        <?php endforeach; ?>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    <?php endif; ?>
 | 
				
			||||||
 | 
					</section>
 | 
				
			||||||
							
								
								
									
										6
									
								
								config/environment.php.example
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								config/environment.php.example
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					define('DB_HOST', '0.0.0.0');
 | 
				
			||||||
 | 
					define('DB_NAME', 'fuel_stats'); 
 | 
				
			||||||
 | 
					define('DB_USER', 'username'); 
 | 
				
			||||||
 | 
					define('DB_PASS', 'password');
 | 
				
			||||||
@@ -16,8 +16,8 @@ class Controller {
 | 
				
			|||||||
     * @param string $viewName
 | 
					     * @param string $viewName
 | 
				
			||||||
     * @param array $data
 | 
					     * @param array $data
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function view($viewName, $data = []) {
 | 
					    public function view($viewName, $data = [], $layout = "base") {
 | 
				
			||||||
        $view = new View();
 | 
					        $view = new View();
 | 
				
			||||||
        $view->render($viewName, $data);
 | 
					        $view->render($viewName, $data, $layout);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -57,7 +57,7 @@ class Database {
 | 
				
			|||||||
     * Check and create required tables if they don't exist
 | 
					     * Check and create required tables if they don't exist
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    private function checkAndCreateTables() {
 | 
					    private function checkAndCreateTables() {
 | 
				
			||||||
        // Create users table
 | 
					        // Create tables
 | 
				
			||||||
        $usersTableQuery = "CREATE TABLE IF NOT EXISTS users (
 | 
					        $usersTableQuery = "CREATE TABLE IF NOT EXISTS users (
 | 
				
			||||||
            id INT AUTO_INCREMENT PRIMARY KEY,
 | 
					            id INT AUTO_INCREMENT PRIMARY KEY,
 | 
				
			||||||
            username VARCHAR(50) NOT NULL,
 | 
					            username VARCHAR(50) NOT NULL,
 | 
				
			||||||
@@ -71,36 +71,56 @@ class Database {
 | 
				
			|||||||
            die("Failed to create users table: " . $this->connection->error);
 | 
					            die("Failed to create users table: " . $this->connection->error);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Create progress table
 | 
					        $vehiclesTableQuery = "
 | 
				
			||||||
        $progressTableQuery = "CREATE TABLE IF NOT EXISTS progress (
 | 
					            CREATE TABLE IF NOT EXISTS vehicles (
 | 
				
			||||||
            id INT AUTO_INCREMENT PRIMARY KEY,
 | 
					                id INT AUTO_INCREMENT PRIMARY KEY,
 | 
				
			||||||
            user_id INT NOT NULL,
 | 
					                user_id INT NOT NULL,
 | 
				
			||||||
            habit_id INT NOT NULL,
 | 
					                name VARCHAR(100) NOT NULL,
 | 
				
			||||||
            date DATE NOT NULL,
 | 
					                registration_plate VARCHAR(50) NOT NULL UNIQUE,
 | 
				
			||||||
            status ENUM('Done', 'Pending') DEFAULT 'Pending',
 | 
					                fuel_type ENUM('Diesel', 'Gasoline 95', 'Gasoline 98', 'Premium Diesel', 'Premium Gasoline 95', 'Premium Gasoline 98', 'Other') NOT NULL,
 | 
				
			||||||
            FOREIGN KEY (user_id) REFERENCES users(id)
 | 
					                note VARCHAR(150) NULL,
 | 
				
			||||||
        ) ENGINE=InnoDB;";
 | 
					                is_default BOOLEAN DEFAULT FALSE NOT NULL,
 | 
				
			||||||
 | 
					                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
 | 
				
			||||||
 | 
					                FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
 | 
				
			||||||
 | 
					            ) ENGINE=InnoDB;
 | 
				
			||||||
 | 
					        ";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (!$this->connection->query($progressTableQuery)) {
 | 
					        if (!$this->connection->query($vehiclesTableQuery)) {
 | 
				
			||||||
            die("Failed to create progress table: " . $this->connection->error);
 | 
					            die("Failed to create vehicles table: " . $this->connection->error);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $refuelingTableQuery = "
 | 
				
			||||||
 | 
					            CREATE TABLE IF NOT EXISTS refueling_records (
 | 
				
			||||||
 | 
					                id INT AUTO_INCREMENT PRIMARY KEY,
 | 
				
			||||||
 | 
					                user_id INT NOT NULL,
 | 
				
			||||||
 | 
					                vehicle_id INT NOT NULL,
 | 
				
			||||||
 | 
					                fuel_type ENUM('Diesel', 'Gasoline 95', 'Gasoline 98', 'Premium Diesel', 'Premium Gasoline 95', 'Premium Gasoline 98', 'Other') NOT NULL,
 | 
				
			||||||
 | 
					                note VARCHAR(150) NULL,
 | 
				
			||||||
 | 
					                liters DOUBLE(10, 2) NOT NULL,
 | 
				
			||||||
 | 
					                price_per_liter DOUBLE(10, 2) NOT NULL,
 | 
				
			||||||
 | 
					                total_price DOUBLE(10, 2) NOT NULL,
 | 
				
			||||||
 | 
					                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
 | 
				
			||||||
 | 
					                FOREIGN KEY (vehicle_id) REFERENCES vehicles(id) ON DELETE CASCADE,
 | 
				
			||||||
 | 
					                FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
 | 
				
			||||||
 | 
					            ) ENGINE=InnoDB;
 | 
				
			||||||
 | 
					        ";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Create habits table
 | 
					        if (!$this->connection->query($refuelingTableQuery)) {
 | 
				
			||||||
        $habitsTableQuery = "CREATE TABLE IF NOT EXISTS habits (
 | 
					            die("Failed to create refueling_records table: " . $this->connection->error);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /*
 | 
				
			||||||
 | 
					        // Create table_name table
 | 
				
			||||||
 | 
					        $usersTableQuery = "CREATE TABLE IF NOT EXISTS table_name (
 | 
				
			||||||
            id INT AUTO_INCREMENT PRIMARY KEY,
 | 
					            id INT AUTO_INCREMENT PRIMARY KEY,
 | 
				
			||||||
            user_id INT NOT NULL,
 | 
					            username VARCHAR(50) NOT NULL,
 | 
				
			||||||
            title VARCHAR(100) NOT NULL,
 | 
					            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
 | 
				
			||||||
            frequency ENUM('Daily', 'Weekly', 'Custom') NOT NULL,
 | 
					 | 
				
			||||||
            custom_frequency VARCHAR(255) DEFAULT NULL, -- Store crontab-like string
 | 
					 | 
				
			||||||
            reward_points INT DEFAULT 1,
 | 
					 | 
				
			||||||
            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
 | 
					 | 
				
			||||||
            FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
 | 
					 | 
				
			||||||
        ) ENGINE=InnoDB;";
 | 
					        ) ENGINE=InnoDB;";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (!$this->connection->query($habitsTableQuery)) {
 | 
					        if (!$this->connection->query($usersTableQuery)) {
 | 
				
			||||||
            die("Failed to create habits table: " . $this->connection->error);
 | 
					            die("Failed to create table_name table: " . $this->connection->error);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        */
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -78,7 +78,7 @@ class Router {
 | 
				
			|||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            http_response_code(404);
 | 
					            http_response_code(404);
 | 
				
			||||||
            $view = new View();
 | 
					            $view = new View();
 | 
				
			||||||
            $view->render('errors/404');
 | 
					            $view->render('errors/404', [], 'noheader');
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,6 +12,15 @@ class Validator {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Check if a field contains numbers
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function number($field, $value, $message = null) {
 | 
				
			||||||
 | 
					        if(!is_numeric($value)) {
 | 
				
			||||||
 | 
					            $this->errors[$field] = $message ?? "$field must be an number";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Check if a field meets minimum length
 | 
					     * Check if a field meets minimum length
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
@@ -46,6 +55,10 @@ class Validator {
 | 
				
			|||||||
        return $this->errors;
 | 
					        return $this->errors;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function setErrors($errors) {
 | 
				
			||||||
 | 
					        $this->errors = $errors;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Check if validation passed
 | 
					     * Check if validation passed
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,27 +1,31 @@
 | 
				
			|||||||
services:
 | 
					services:
 | 
				
			||||||
  mariadb:
 | 
					  mariadb:
 | 
				
			||||||
    image: mariadb:11.4 # LTS at 25. 12. 2025
 | 
					    image: mariadb:11.4 # LTS at 25. 12. 2025
 | 
				
			||||||
    restart: always
 | 
					    restart: on-failure:2
 | 
				
			||||||
    environment:
 | 
					    environment:
 | 
				
			||||||
      MARIADB_ROOT_PASSWORD: root
 | 
					      MARIADB_ROOT_PASSWORD: root
 | 
				
			||||||
    ports:
 | 
					    ports:
 | 
				
			||||||
      - 3306:3306
 | 
					      - 3306:3306
 | 
				
			||||||
 | 
					    profiles: ["prod", "dev"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  phpmyadmin:
 | 
					  phpmyadmin:
 | 
				
			||||||
    image: phpmyadmin
 | 
					    image: phpmyadmin
 | 
				
			||||||
    restart: always
 | 
					    restart: on-failure:2
 | 
				
			||||||
    ports:
 | 
					    ports:
 | 
				
			||||||
      - 8080:80
 | 
					      - 8080:80
 | 
				
			||||||
    environment:
 | 
					    environment:
 | 
				
			||||||
      - PMA_ARBITRARY=1
 | 
					      - PMA_ARBITRARY=1
 | 
				
			||||||
 | 
					    profiles: ["dev"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  habittracker:
 | 
					  fuelstats:
 | 
				
			||||||
    build:
 | 
					    build:
 | 
				
			||||||
      context: .
 | 
					      context: .
 | 
				
			||||||
      dockerfile: Dockerfile
 | 
					      dockerfile: Dockerfile
 | 
				
			||||||
    #volumes:
 | 
					    volumes:
 | 
				
			||||||
    #  - .:/var/www/html
 | 
					      - .:/var/www/html
 | 
				
			||||||
    ports:
 | 
					    ports:
 | 
				
			||||||
      - 8000:80
 | 
					      - 8000:80
 | 
				
			||||||
    depends_on:
 | 
					    depends_on:
 | 
				
			||||||
      - mariadb
 | 
					      - mariadb
 | 
				
			||||||
 | 
					    restart: on-failure:2
 | 
				
			||||||
 | 
					    profiles: ["prod"]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,6 @@
 | 
				
			|||||||
.dashboard {
 | 
					.dashboard {
 | 
				
			||||||
  display: flex;
 | 
					  display: flex;
 | 
				
			||||||
  flex-direction: column;
 | 
					  flex-direction: column;
 | 
				
			||||||
  width: 100vw;
 | 
					 | 
				
			||||||
  align-items: center;
 | 
					  align-items: center;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -17,6 +16,6 @@
 | 
				
			|||||||
  background-color: var(--clr-secondary);
 | 
					  background-color: var(--clr-secondary);
 | 
				
			||||||
  border-radius: var(--border-radious);
 | 
					  border-radius: var(--border-radious);
 | 
				
			||||||
  border: var(--borderWidth-thin) solid var(--clr-border);
 | 
					  border: var(--borderWidth-thin) solid var(--clr-border);
 | 
				
			||||||
  min-width: 17rem;
 | 
					  width: 18rem;
 | 
				
			||||||
  padding: 1rem;
 | 
					  padding: 1rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,7 +3,6 @@
 | 
				
			|||||||
  flex-direction: column;
 | 
					  flex-direction: column;
 | 
				
			||||||
  align-items: center;
 | 
					  align-items: center;
 | 
				
			||||||
  justify-content: center;
 | 
					  justify-content: center;
 | 
				
			||||||
  width: 100vw;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
body {
 | 
					body {
 | 
				
			||||||
@@ -59,3 +58,21 @@ select {
 | 
				
			|||||||
.form small.error {
 | 
					.form small.error {
 | 
				
			||||||
  width: 15rem;
 | 
					  width: 15rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.form .bordered {
 | 
				
			||||||
 | 
					  border-radius: var(--border-radious);
 | 
				
			||||||
 | 
					  border: var(--borderWidth-thin) solid var(--clr-border);
 | 
				
			||||||
 | 
					  width: 17rem;
 | 
				
			||||||
 | 
					  padding: 1rem;
 | 
				
			||||||
 | 
					  margin-top: 1rem;
 | 
				
			||||||
 | 
					  text-align: center;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.form .bordered a {
 | 
				
			||||||
 | 
					  text-decoration: none;
 | 
				
			||||||
 | 
					  color: var(--clr-link-blue);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.form .bordered a:hover {
 | 
				
			||||||
 | 
					  text-decoration: underline;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,32 +1,48 @@
 | 
				
			|||||||
@import url("https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300..800;1,300..800&family=Source+Code+Pro:ital,wght@0,200..900;1,200..900&display=swap");
 | 
					@import url("https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300..800;1,300..800&family=Source+Code+Pro:ital,wght@0,200..900;1,200..900&display=swap");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
body {
 | 
					body {
 | 
				
			||||||
  font-family: "Open Sans", serif;
 | 
						font-family: "Open Sans", serif;
 | 
				
			||||||
  background-color: var(--clr-primary);
 | 
						background-color: var(--clr-primary);
 | 
				
			||||||
  color: white;
 | 
						color: white;
 | 
				
			||||||
  font-size: 14px;
 | 
						font-size: 14px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
a {
 | 
					a {
 | 
				
			||||||
  color: white;
 | 
						color: white;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
h1 {
 | 
					h1 {
 | 
				
			||||||
  margin-top: .5rem;
 | 
						margin-top: .5rem;
 | 
				
			||||||
  margin-bottom: 1rem;
 | 
						margin-bottom: 1rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.btn-primary,
 | 
					.btn-primary,
 | 
				
			||||||
 | 
					.btn-secondary,
 | 
				
			||||||
 | 
					.btn-tertiary,
 | 
				
			||||||
 | 
					.btn-green,
 | 
				
			||||||
.btn-danger {
 | 
					.btn-danger {
 | 
				
			||||||
  background-color: var(--clr-green);
 | 
						background-color: var(--clr-primary);
 | 
				
			||||||
  padding: .5rem;
 | 
						padding: .5rem;
 | 
				
			||||||
  text-decoration: none;
 | 
						text-decoration: none;
 | 
				
			||||||
  cursor: pointer;
 | 
						cursor: pointer;
 | 
				
			||||||
  border-radius: var(--border-radious);
 | 
						border-radius: var(--border-radious);
 | 
				
			||||||
  border: var(--borderWidth-thin) solid var(--clr-border);
 | 
						border: var(--borderWidth-thin) solid var(--clr-border);
 | 
				
			||||||
 | 
						color: white;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.btn-secondary {
 | 
				
			||||||
 | 
						background-color: var(--clr-secondary);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.btn-tertiary {
 | 
				
			||||||
 | 
						background-color: var(--clr-tertiary);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.btn-green {
 | 
				
			||||||
 | 
						background-color: var(--clr-green);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.btn-danger {
 | 
					.btn-danger {
 | 
				
			||||||
  background-color: var(--clr-danger-muted) !important;
 | 
						background-color: var(--clr-danger-muted);
 | 
				
			||||||
  border: var(--borderWidth-thin) solid var(--clr-border-danger);
 | 
						border: var(--borderWidth-thin) solid var(--clr-border-danger);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,21 +0,0 @@
 | 
				
			|||||||
.form form .dow_chb_wrapper input[type="checkbox"] {
 | 
					 | 
				
			||||||
  width: 1rem;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.form form .dow_chb_wrapper {
 | 
					 | 
				
			||||||
  display: flex;
 | 
					 | 
				
			||||||
  justify-content: space-between;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#lbl_dow {
 | 
					 | 
				
			||||||
  margin-bottom: .5rem;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#lbl_dom {
 | 
					 | 
				
			||||||
  margin-top: .5rem;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#custom-frequency {
 | 
					 | 
				
			||||||
  flex-direction: column;
 | 
					 | 
				
			||||||
  justify-content: space-between;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,26 +0,0 @@
 | 
				
			|||||||
.habits h1 {
 | 
					 | 
				
			||||||
  font-size: 2rem;
 | 
					 | 
				
			||||||
  margin-bottom: 1rem;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.habits table {
 | 
					 | 
				
			||||||
  width: 100%;
 | 
					 | 
				
			||||||
  border-collapse: collapse;
 | 
					 | 
				
			||||||
  margin-top: 1rem;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.habits table th,
 | 
					 | 
				
			||||||
.habits table td {
 | 
					 | 
				
			||||||
  border: 1px solid #ccc;
 | 
					 | 
				
			||||||
  padding: 8px;
 | 
					 | 
				
			||||||
  text-align: left;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.habits a {
 | 
					 | 
				
			||||||
  color: #007bff;
 | 
					 | 
				
			||||||
  text-decoration: none;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.habits a:hover {
 | 
					 | 
				
			||||||
  text-decoration: underline;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										26
									
								
								public/css/header.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								public/css/header.css
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
				
			|||||||
 | 
					header {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  justify-content: space-between;
 | 
				
			||||||
 | 
					  padding: 0 2rem;
 | 
				
			||||||
 | 
					  width: 100vw;
 | 
				
			||||||
 | 
					  height: 3rem;
 | 
				
			||||||
 | 
					  background: var(--clr-secondary);
 | 
				
			||||||
 | 
					  border-radius: var(--border-radious);
 | 
				
			||||||
 | 
					  border-bottom: var(--borderWidth-thin) solid var(--clr-border);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#hd-left,
 | 
				
			||||||
 | 
					#hd-right {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  gap: 1rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					header a img {
 | 
				
			||||||
 | 
					  height: 2rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					header a {
 | 
				
			||||||
 | 
					  text-decoration: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
main {
 | 
					main {
 | 
				
			||||||
  display: flex;
 | 
					  display: flex;
 | 
				
			||||||
  gap: 2rem;
 | 
					  flex-direction: column;
 | 
				
			||||||
  margin-top: 2rem;
 | 
					  margin-top: 2rem;
 | 
				
			||||||
  margin-bottom: 2rem;
 | 
					  margin-bottom: 2rem;
 | 
				
			||||||
  padding: 0 var(--container-size);
 | 
					  padding: 0 var(--container-size);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,13 @@
 | 
				
			|||||||
  --clr-green: #238636;
 | 
					  --clr-green: #238636;
 | 
				
			||||||
  --clr-danger-muted: #f851491a;
 | 
					  --clr-danger-muted: #f851491a;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  --clr-link-blue: #4493f8;
 | 
				
			||||||
 | 
					  --clr-light-blue: #39a2ae;
 | 
				
			||||||
 | 
					  --clr-light-green: #71f79f;
 | 
				
			||||||
 | 
					  --clr-red: #9b1d20;
 | 
				
			||||||
 | 
					  --clr-orange: #e08e45;
 | 
				
			||||||
 | 
					  --clr-gray-blue: #627c85;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  --border-radious: 5px;
 | 
					  --border-radious: 5px;
 | 
				
			||||||
  --borderWidth-thin: max(1px, 0.0625rem);
 | 
					  --borderWidth-thin: max(1px, 0.0625rem);
 | 
				
			||||||
  --clr-border: #3d444db3;
 | 
					  --clr-border: #3d444db3;
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										3
									
								
								public/css/vehicle_create.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								public/css/vehicle_create.css
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					#registration_plate {
 | 
				
			||||||
 | 
					  text-transform: uppercase;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										29
									
								
								public/css/vehicles.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								public/css/vehicles.css
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					.vehicle-wrapper {
 | 
				
			||||||
 | 
						align-items: center;
 | 
				
			||||||
 | 
						display: flex;
 | 
				
			||||||
 | 
						flex-wrap: wrap;
 | 
				
			||||||
 | 
						gap: 1rem;
 | 
				
			||||||
 | 
						justify-content: center;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.vehicle {
 | 
				
			||||||
 | 
						display: flex;
 | 
				
			||||||
 | 
						flex-direction: column;
 | 
				
			||||||
 | 
						align-items: center;
 | 
				
			||||||
 | 
						justify-content: center;
 | 
				
			||||||
 | 
						background-color: var(--clr-secondary);
 | 
				
			||||||
 | 
						border-radius: var(--border-radious);
 | 
				
			||||||
 | 
						border: var(--borderWidth-thin) solid var(--clr-border);
 | 
				
			||||||
 | 
						width: 17rem;
 | 
				
			||||||
 | 
						padding: 1rem;
 | 
				
			||||||
 | 
						margin-top: 1rem;
 | 
				
			||||||
 | 
						text-align: center;
 | 
				
			||||||
 | 
						min-height: 8rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.btn-wrapper {
 | 
				
			||||||
 | 
						display: flex;
 | 
				
			||||||
 | 
						justify-content: center;
 | 
				
			||||||
 | 
						align-items: center;
 | 
				
			||||||
 | 
						margin-bottom: 1rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -20,7 +20,8 @@ require_once '../core/Database.php';
 | 
				
			|||||||
require_once '../core/middlewares/RequireAuth.php';
 | 
					require_once '../core/middlewares/RequireAuth.php';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require_once models . 'User.php';
 | 
					require_once models . 'User.php';
 | 
				
			||||||
require_once models . 'Habit.php';
 | 
					require_once models . 'Vehicle.php';
 | 
				
			||||||
 | 
					require_once models . 'Refuel.php';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Initialize router
 | 
					// Initialize router
 | 
				
			||||||
$router = new Router();
 | 
					$router = new Router();
 | 
				
			||||||
@@ -40,12 +41,16 @@ $router->group('/auth', [], function ($router) {
 | 
				
			|||||||
// dashboard route
 | 
					// dashboard route
 | 
				
			||||||
$router->add('/dashboard', 'DashboardController@index', ['RequireAuth']);
 | 
					$router->add('/dashboard', 'DashboardController@index', ['RequireAuth']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// habits routes
 | 
					// vehicle routes
 | 
				
			||||||
$router->group('/habits', ['RequireAuth'], function ($router) {
 | 
					$router->group('/vehicles', ['RequireAuth'], function ($router) {
 | 
				
			||||||
    $router->add('', 'HabitController@index');
 | 
					    $router->add('', 'VehicleController@index');
 | 
				
			||||||
    $router->add('/create', 'HabitController@create');
 | 
					    $router->add('/create', 'VehicleController@create');
 | 
				
			||||||
    $router->add('/edit/{id}', 'HabitController@edit');
 | 
					    $router->add('/edit/{id}', 'VehicleController@edit');
 | 
				
			||||||
    $router->add('/delete/{id}', 'HabitController@delete');
 | 
					    $router->add('/delete/{id}', 'VehicleController@delete');
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					$router->group('/refuel', ['RequireAuth'], function ($router) {
 | 
				
			||||||
 | 
					    $router->add('/create', 'RefuelController@create');
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$router->dispatch();
 | 
					$router->dispatch();
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										98
									
								
								public/js/offline-records.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								public/js/offline-records.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,98 @@
 | 
				
			|||||||
 | 
					async function checkOnline() {
 | 
				
			||||||
 | 
						if (!navigator.onLine) {
 | 
				
			||||||
 | 
							console.log("Offline (no network connection)");
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						try {
 | 
				
			||||||
 | 
							const response = await fetch(
 | 
				
			||||||
 | 
								"https://www.google.com/favicon.ico?" + new Date().getTime(),
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									mode: "no-cors",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						} catch (error) {
 | 
				
			||||||
 | 
							console.log("Connected to network but no internet access");
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					setInterval(async () => {
 | 
				
			||||||
 | 
						const isOnline = await checkOnline();
 | 
				
			||||||
 | 
						if (!isOnline) {
 | 
				
			||||||
 | 
							console.log("OFFLINE!!!");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}, 5000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const offbtn = document.querySelector("#btn-offline-add");
 | 
				
			||||||
 | 
					offbtn.addEventListener("click", (e) => {
 | 
				
			||||||
 | 
						e.preventDefault();
 | 
				
			||||||
 | 
						document.querySelector("section.dashboard").style.display = "none";
 | 
				
			||||||
 | 
						const offline = document.createElement("div");
 | 
				
			||||||
 | 
						offline.classList.add("offline");
 | 
				
			||||||
 | 
						offline.innerHTML = `
 | 
				
			||||||
 | 
					    <b>You're Offline</b>
 | 
				
			||||||
 | 
					    <p>You can create an fuel record locally on your device and sync it later</p>
 | 
				
			||||||
 | 
					    <section class="form">
 | 
				
			||||||
 | 
					        <h1 class="header-form"><?= $this->get('title') ?></h1>
 | 
				
			||||||
 | 
					        <!-- <?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>
 | 
				
			||||||
 | 
					            <select name="vehicle" id="vehicle">
 | 
				
			||||||
 | 
					            <!-- <?php foreach ($this->get('vehicles') as $vehicle): ?> -->
 | 
				
			||||||
 | 
					            <!--     <option value="<?= $vehicle['id'] ?>"><?= $vehicle['name'] . " | " . $vehicle['registration_plate'] ?></option> -->
 | 
				
			||||||
 | 
					            <!-- <?php endforeach; ?> -->
 | 
				
			||||||
 | 
					            </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>
 | 
				
			||||||
 | 
					            <select name="fuel_type" id="fuel_type">
 | 
				
			||||||
 | 
					                <option value="Diesel">Diesel</option>
 | 
				
			||||||
 | 
					                <option value="Gasoline 95">Gasoline 95</option>
 | 
				
			||||||
 | 
					                <option value="Gasoline 98">Gasoline 98</option>
 | 
				
			||||||
 | 
					                <option value="Premium Diesel">Premium Diesel</option>
 | 
				
			||||||
 | 
					                <option value="Premium Gasoline 95">Premium Gasoline 95</option>
 | 
				
			||||||
 | 
					                <option value="Premium Gasoline 98">Premium Gasoline 98</option>
 | 
				
			||||||
 | 
					                <option value="Other">Other</option>
 | 
				
			||||||
 | 
					            </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>
 | 
				
			||||||
 | 
					            <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> -->
 | 
				
			||||||
 | 
					            <!-- <?php endif; ?> -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <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') ?>">
 | 
				
			||||||
 | 
					            <!-- <?php if (isset($this->get('validationErrors')['price_per_liter'])): ?> -->
 | 
				
			||||||
 | 
					            <!--     <small class="error"><?= $this->get('validationErrors')['price_per_liter'] ?></small> -->
 | 
				
			||||||
 | 
					            <!-- <?php endif; ?> -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <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') ?>">
 | 
				
			||||||
 | 
					            <!-- <?php if (isset($this->get('validationErrors')['total_price'])): ?> -->
 | 
				
			||||||
 | 
					            <!--     <small class="error"><?= $this->get('validationErrors')['total_price'] ?></small> -->
 | 
				
			||||||
 | 
					            <!-- <?php endif; ?> -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <label for="note">Note</label>
 | 
				
			||||||
 | 
					            <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> -->
 | 
				
			||||||
 | 
					            <!-- <?php endif; ?> -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <input type="submit" value="Create fuel record">
 | 
				
			||||||
 | 
					        </form>
 | 
				
			||||||
 | 
					    </section>
 | 
				
			||||||
 | 
					  `;
 | 
				
			||||||
 | 
						document.querySelector("main").appendChild(offline);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
		Reference in New Issue
	
	Block a user