diff --git a/docker/database/start-scripts/1-seed.sql b/docker/database/start-scripts/1-seed.sql index d18efacf..5cff1847 100644 --- a/docker/database/start-scripts/1-seed.sql +++ b/docker/database/start-scripts/1-seed.sql @@ -3,10 +3,10 @@ INSERT INTO roles (name) VALUES ('Administrador'), ('Gerente'), ('Trabajador'); -INSERT INTO workers (company, name, dni, password, email, role_id, created_at, updated_at, deleted_at) VALUES -('TechCorp', 'Carlos García', '12345678A', 'hashedpassword1', 'carlos.garcia@example.com', 1, NOW(), NOW(), NULL), -('InnovaTech', 'Ana Martínez', '23456789B', 'hashedpassword2', 'ana.martinez@example.com', 2, NOW(), NOW(), NULL), -('DesignWorks', 'José Rodríguez', '34567890C', 'hashedpassword3', 'jose.rodriguez@example.com', 3, NOW(), NOW(), NULL); +INSERT INTO workers (company, name, dni, password, email, role_id) VALUES +('TechCorp', 'Carlos García', '12345678A', 'hashedpassword1', 'carlos.garcia@example.com', 1), +('InnovaTech', 'Ana Martínez', '23456789B', 'hashedpassword2', 'ana.martinez@example.com', 2), +('DesignWorks', 'José Rodríguez', '34567890C', 'hashedpassword3', 'jose.rodriguez@example.com', 3); INSERT INTO contracts (name, start_date, end_date, invoice_proposed, invoice_agreed, invoice_paid) VALUES ('Ayuntamiento de Valencia', '2021-01-01', '2021-12-31', 1000.00, 900.00, 900.00), ('Administración General del Estado', '2021-01-01', '2021-12-31', 2000.00, 1800.00, 1800.00), @@ -50,7 +50,7 @@ INSERT INTO incidences (name, element_id, description) VALUES ('Banco pintado', 2, 'Banco pintado con grafitis'), ('Fuente con fuga', 3, 'Fuente con fuga de agua'); --TODO: tasks, routes and works -INSERT INTO work_orders (contract_id) VALUES +INSERT INTO work_orders (contract_id) VALUES (1), (2), (3); diff --git a/src/app/Controllers/AuthController.php b/src/app/Controllers/AuthController.php new file mode 100644 index 00000000..a4bd1bc8 --- /dev/null +++ b/src/app/Controllers/AuthController.php @@ -0,0 +1,66 @@ + 'Auth/Login', + 'title' => 'Login Page', + 'layout' => 'AuthLayout', + 'data' => [ + 'error' => Session::get('error'), + ], + ]); + + // Clear the error message after displaying it + Session::remove('error'); + } + + public function login($postData) + { + $email = $postData['email'] ?? null; + $password = $postData['password'] ?? null; + + if (! $email || ! $password) { + // Redirect back with error if fields are missing + Session::set('error', 'Email and password are required.'); + header('Location: /auth/login'); + exit; + } + + // Check if the user exists and password matches + $user = User::findBy(['email' => $email, 'password' => $password], true); + + if (! $user || strcmp($user->password, $password) !== 0) { // TODO: Verify hashed password not raw password + echo 'Invalid email or password.'; + // Redirect back with error if authentication fails + Session::set('error', 'Invalid email or password.'); + header('Location: /auth/login'); + exit; + } + + Session::set('user', [ + 'id' => $user->getId(), + 'name' => $user->name, + 'email' => $user->email, + 'role_id' => $user->role_id, + ]); + + header('Location: /'); + exit; + } + + public function logout() + { + Session::destroy(); + header('Location: /auth/login'); + exit; + } +} diff --git a/src/app/Controllers/CAuth.php b/src/app/Controllers/CAuth.php deleted file mode 100644 index 1725657b..00000000 --- a/src/app/Controllers/CAuth.php +++ /dev/null @@ -1,18 +0,0 @@ - "Auth/Login", - "title" => "Login Page", - "layout" => "AuthLayout", - "data" => [] - ]); - } -} diff --git a/src/app/Controllers/CContract.php b/src/app/Controllers/CContract.php deleted file mode 100644 index 3f79c7b1..00000000 --- a/src/app/Controllers/CContract.php +++ /dev/null @@ -1,20 +0,0 @@ - "Contracts", - "title" => "Contracts", - "layout" => "MainLayout", - "data" => ["contracts" => $contracts] - ]); - } -} diff --git a/src/app/Controllers/CElement.php b/src/app/Controllers/CElement.php deleted file mode 100644 index 563d73f3..00000000 --- a/src/app/Controllers/CElement.php +++ /dev/null @@ -1,20 +0,0 @@ - "Element", - "title" => "Element", - "layout" => "MainLayout", - "data" => ["elements" => $elements] - ]); - } -} diff --git a/src/app/Controllers/CHome.php b/src/app/Controllers/CHome.php deleted file mode 100644 index a67a2fc3..00000000 --- a/src/app/Controllers/CHome.php +++ /dev/null @@ -1,20 +0,0 @@ - "Home", - "title" => "Home Page", - "layout" => "MainLayout", - "data" => ["workers" => $workers] - ]); - } -} diff --git a/src/app/Controllers/CIncidence.php b/src/app/Controllers/CIncidence.php deleted file mode 100644 index bb65b3eb..00000000 --- a/src/app/Controllers/CIncidence.php +++ /dev/null @@ -1,45 +0,0 @@ - "Incidence", - "title" => "Incidences", - "layout" => "MainLayout", - ]); - } - - public function findall() - { - $incidences = MIncidence::findAll(); - - View::render([ - "view" => "Incidence/SeeAllIncidences", - "title" => "Incidences", - "layout" => "MainLayout", - "data" => ["incidences" => $incidences] - ]); - } - - public function get() - { - $elements = MElement::findAll(); - - View::render([ - "view" => "Incidence/Create", - "title" => "Create Incidence", - "layout" => "MainLayout", - "data" => [ - "elements" => $elements, - ] - ]); - } -} diff --git a/src/app/Controllers/CPruningType.php b/src/app/Controllers/CPruningType.php deleted file mode 100644 index 854f5721..00000000 --- a/src/app/Controllers/CPruningType.php +++ /dev/null @@ -1,20 +0,0 @@ - "PruningType", - "title" => "Pruning Types", - "layout" => "MainLayout", - "data" => ["pruning_types" => $pruning_types] - ]); - } -} diff --git a/src/app/Controllers/CTaskType.php b/src/app/Controllers/CTaskType.php deleted file mode 100644 index 33ed892b..00000000 --- a/src/app/Controllers/CTaskType.php +++ /dev/null @@ -1,20 +0,0 @@ - "TaskTypes/index", - "title" => "Task Types", - "layout" => "MainLayout", - "data" => ["task_types" => $task_types] - ]); - } -} diff --git a/src/app/Controllers/CTreeType.php b/src/app/Controllers/CTreeType.php deleted file mode 100644 index 78632dc5..00000000 --- a/src/app/Controllers/CTreeType.php +++ /dev/null @@ -1,20 +0,0 @@ - "TreeType", - "title" => "Tree Types", - "layout" => "MainLayout", - "data" => ["tree_types" => $tree_types] - ]); - } -} diff --git a/src/app/Controllers/CWorkOrder.php b/src/app/Controllers/CWorkOrder.php deleted file mode 100644 index 6bf06e08..00000000 --- a/src/app/Controllers/CWorkOrder.php +++ /dev/null @@ -1,60 +0,0 @@ - "order/index", - "title" => "Order", - "layout" => "MainLayout", - "data" => ["orders" => $orders] - ]); - } - - public function find() - { - $id = $_GET['id']; - $order = MWorkOrder::find($id); - View::render([ - "view" => "order", - "title" => "Order", - "layout" => "MainLayout", - "data" => ["order" => $order] - ]); - } - - public function indexCreate() - { - $orders = MWorkOrder::findAll(); - View::render([ - "view" => "order/create", - "title" => "Create Order", - "layout" => "MainLayout", - "data" => ["orders" => $orders] - ]); - } - - public function save() - { - $order = new MWorkOrder(); - - $order->save(); - } - - public function delete() - { - $id = $_GET['id']; - $order = MWorkOrder::find($id); - $order->delete(); - } - - public function update() - { - - } -} \ No newline at end of file diff --git a/src/app/Controllers/CZone.php b/src/app/Controllers/CZone.php deleted file mode 100644 index 07223c97..00000000 --- a/src/app/Controllers/CZone.php +++ /dev/null @@ -1,20 +0,0 @@ - "Zone", - "title" => "Zones", - "layout" => "MainLayout", - "data" => ["zones" => $zones] - ]); - } -} diff --git a/src/app/Controllers/ContractController.php b/src/app/Controllers/ContractController.php new file mode 100644 index 00000000..5bc48649 --- /dev/null +++ b/src/app/Controllers/ContractController.php @@ -0,0 +1,20 @@ + 'Contracts', + 'title' => 'Contracts', + 'layout' => 'MainLayout', + 'data' => ['contracts' => $contracts], + ]); + } +} diff --git a/src/app/Controllers/DashboardController.php b/src/app/Controllers/DashboardController.php new file mode 100644 index 00000000..b1dc8389 --- /dev/null +++ b/src/app/Controllers/DashboardController.php @@ -0,0 +1,18 @@ + 'Dashboard', + 'title' => 'Dashboard', + 'layout' => 'MainLayout', + 'data' => [], + ]); + } +} diff --git a/src/app/Controllers/ElementController.php b/src/app/Controllers/ElementController.php new file mode 100644 index 00000000..d88b8416 --- /dev/null +++ b/src/app/Controllers/ElementController.php @@ -0,0 +1,20 @@ + 'Elements', + 'title' => 'Element', + 'layout' => 'MainLayout', + 'data' => ['elements' => $elements], + ]); + } +} diff --git a/src/app/Controllers/IncidenceController.php b/src/app/Controllers/IncidenceController.php new file mode 100644 index 00000000..79115b81 --- /dev/null +++ b/src/app/Controllers/IncidenceController.php @@ -0,0 +1,44 @@ + 'Incidence', + 'title' => 'Incidences', + 'layout' => 'MainLayout', + ]); + } + + public function findall() + { + $incidences = Incidence::findAll(); + View::render([ + 'view' => 'Incidence/SeeAllIncidences', + 'title' => 'Incidences', + 'layout' => 'MainLayout', + 'data' => ['incidences' => $incidences], + ]); + } + + public function get() + { + $elements = Element::findAll(); + + View::render([ + 'view' => 'Incidence/Create', + 'title' => 'Create Incidence', + 'layout' => 'MainLayout', + 'data' => [ + 'elements' => $elements, + ], + ]); + } +} diff --git a/src/app/Controllers/PruningTypeController.php b/src/app/Controllers/PruningTypeController.php new file mode 100644 index 00000000..24a8ea2e --- /dev/null +++ b/src/app/Controllers/PruningTypeController.php @@ -0,0 +1,20 @@ + 'PruningType', + 'title' => 'Pruning Types', + 'layout' => 'MainLayout', + 'data' => ['pruning_types' => $pruning_types], + ]); + } +} diff --git a/src/app/Controllers/TaskTypeController.php b/src/app/Controllers/TaskTypeController.php new file mode 100644 index 00000000..b0629da2 --- /dev/null +++ b/src/app/Controllers/TaskTypeController.php @@ -0,0 +1,20 @@ + 'TaskTypes/index', + 'title' => 'Task Types', + 'layout' => 'MainLayout', + 'data' => ['task_types' => $task_types], + ]); + } +} diff --git a/src/app/Controllers/TreeTypeController.php b/src/app/Controllers/TreeTypeController.php new file mode 100644 index 00000000..2d13e2f2 --- /dev/null +++ b/src/app/Controllers/TreeTypeController.php @@ -0,0 +1,20 @@ + 'TreeTypes', + 'title' => 'Tree Types', + 'layout' => 'MainLayout', + 'data' => ['tree_types' => $tree_types], + ]); + } +} diff --git a/src/app/Controllers/UserController.php b/src/app/Controllers/UserController.php new file mode 100644 index 00000000..956f45ec --- /dev/null +++ b/src/app/Controllers/UserController.php @@ -0,0 +1,90 @@ + 'Users', + 'title' => 'Manage Users', + 'layout' => 'MainLayout', + 'data' => ['users' => $elements], + ]); + + Session::remove('success'); + } + + public function create() + { + View::render([ + 'view' => 'User/Create', + 'title' => 'Add User', + 'layout' => 'MainLayout', + 'data' => [], + ]); + } + + public function store($postData) + { + $user = new User; + $user->company = $postData['company']; + $user->name = $postData['name']; + $user->dni = $postData['dni']; + $user->email = $postData['email']; + + // Check if password is not empty before updating + if (! empty($postData['password'])) { + $user->password = $postData['password']; + } + + $user->role_id = $postData['role_id']; + $user->save(); + + Session::set('success', 'User created successfully'); + + header('Location: /users'); + } + + public function edit($id) + { + $user = User::find($id); + View::render([ + 'view' => 'User/Edit', + 'title' => 'Edit User', + 'layout' => 'MainLayout', + 'data' => ['user' => $user], + ]); + } + + public function update($id, $postData) + { + $user = User::find($id); + $user->company = $postData['company']; + $user->name = $postData['name']; + $user->dni = $postData['dni']; + $user->email = $postData['email']; + $user->role_id = $postData['role_id']; + $user->save(); + + Session::set('success', 'User updated successfully'); + + header('Location: /users'); + } + + public function destroy($id) + { + $user = User::find($id); + $user->delete(); + + Session::set('success', 'User deleted successfully'); + + header('Location: /users'); + } +} diff --git a/src/app/Controllers/WorkOrderController.php b/src/app/Controllers/WorkOrderController.php new file mode 100644 index 00000000..009d209b --- /dev/null +++ b/src/app/Controllers/WorkOrderController.php @@ -0,0 +1,66 @@ + 'WorkOrders', + 'title' => 'Manage Orders', + 'layout' => 'MainLayout', + 'data' => ['workOrders' => $workOrders], + ]); + Session::remove('success'); + } + + public function create() + { + View::render([ + 'view' => 'Order/Create', + 'title' => 'Add Order', + 'layout' => 'MainLayout', + 'data' => [], + ]); + } + + public function store($postData) {} + + public function edit($id) + { + $order = WorkOrder::find($id); + View::render([ + 'view' => 'Order/Edit', + 'title' => 'Edit Order', + 'layout' => 'MainLayout', + 'data' => ['order' => $order], + ]); + } + + public function update($id, $postData) + { + $order = WorkOrder::find($id); + + $order->save(); + + Session::set('success', 'Order updated successfully'); + + header('Location: /orders'); + } + + public function destroy($id) + { + $order = WorkOrder::find($id); + $order->delete(); + + Session::set('success', 'Order deleted successfully'); + + header('Location: /orders'); + } +} diff --git a/src/app/Controllers/ZoneController.php b/src/app/Controllers/ZoneController.php new file mode 100644 index 00000000..47bef0a9 --- /dev/null +++ b/src/app/Controllers/ZoneController.php @@ -0,0 +1,74 @@ + 'Zones', + 'title' => 'Zones', + 'layout' => 'MainLayout', + 'data' => ['zones' => $zones], + ]); + } + + public function create() + { + View::render([ + 'view' => 'Zone/Create', + 'title' => 'Add Zone', + 'layout' => 'MainLayout', + 'data' => [], + ]); + } + + public function store($postData) + { + $zone = new Zone; + $zone->name = $postData['name']; + $zone->postal_code = $postData['postal_code']; + $zone->point_id = $postData['point_id']; + + $zone->save(); + + header('Location: /zones'); + } + + public function edit($id) + { + $zone = Zone::find($id); + View::render([ + 'view' => 'Zone/Edit', + 'title' => 'Edit Zone', + 'layout' => 'MainLayout', + 'data' => ['zone' => $zone], + ]); + } + + public function update($id, $postData) + { + $zone = Zone::find($id); + + $zone->name = $postData['name']; + $zone->postal_code = $postData['postal_code']; + $zone->point_id = $postData['point_id']; + + $zone->save(); + + header('Location: /zones'); + } + + public function destroy($id) + { + $zone = Zone::find($id); + $zone->delete(); + + header('Location: /zones'); + } +} diff --git a/src/app/Core/Database.php b/src/app/Core/Database.php index b4b96ddf..d4ea9581 100755 --- a/src/app/Core/Database.php +++ b/src/app/Core/Database.php @@ -2,31 +2,33 @@ namespace App\Core; +use Exception; use PDO; use PDOException; class Database { - private static $instance = null; + private static $instance; public static function connect() { - if (!self::$instance) { + if (! self::$instance) { try { // Read the password from the file $db_pass = trim(file_get_contents(getenv('DB_PASS_FILE_PATH'))); self::$instance = new PDO( - "mysql:host=" . getenv('DB_HOST') . ";dbname=" . getenv('DB_NAME'), + 'mysql:host='.getenv('DB_HOST').';dbname='.getenv('DB_NAME'), getenv('DB_USER'), $db_pass ); self::$instance->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } catch (PDOException $e) { - Logger::log("Database connection error: " . $e->getMessage()); - throw new \Exception("Database connection failed."); + Logger::log('Database connection error: '.$e->getMessage()); + throw new Exception('Database connection failed.'); } } + return self::$instance; } @@ -36,10 +38,10 @@ public static function prepareAndExecute($query, $params = [], $fetchMode = PDO: try { $stmt = $db->prepare($query); $stmt->execute($params); + return $stmt->fetchAll($fetchMode); } catch (PDOException $e) { - Logger::log("Database query error: " . $e->getMessage()); - throw new \Exception("Query failed."); + return null; } } } diff --git a/src/app/Core/Logger.php b/src/app/Core/Logger.php index f96ed199..4bdebfc2 100755 --- a/src/app/Core/Logger.php +++ b/src/app/Core/Logger.php @@ -6,9 +6,9 @@ class Logger { public static function log($message, $level = 'info') { - $logFile = getenv("LOG_FILE_PATH"); - $logMessage = strtoupper($level) . ' - ' . date('Y-m-d H:i:s') . ' - ' . $message . PHP_EOL; - if (!file_exists($logFile)) { + $logFile = getenv('LOG_FILE_PATH'); + $logMessage = strtoupper($level).' - '.date('Y-m-d H:i:s').' - '.$message.PHP_EOL; + if (! file_exists($logFile)) { touch($logFile); } file_put_contents($logFile, $logMessage, FILE_APPEND); diff --git a/src/app/Core/Router.php b/src/app/Core/Router.php index b3b25c58..cc4a83f5 100755 --- a/src/app/Core/Router.php +++ b/src/app/Core/Router.php @@ -2,53 +2,108 @@ namespace App\Core; +use App\Middlewares\MiddlewareInterface; +use Exception; + class Router { - protected $routes = []; + protected const HTTP_METHODS = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH']; + + protected array $routes = []; - public function load($file) + public function load(string $file): void { - $routes = include $file; - $this->routes = $routes; + $this->routes = include $file; } - public function dispatch($requestMethod, $requestUri) + public function dispatch(string $requestMethod, string $requestUri, array $postData = []): void { - // Check for direct match first + if (! in_array($requestMethod, self::HTTP_METHODS)) { + $this->abort(405, 'Method Not Allowed'); + + return; + } + + // Match exact route if (isset($this->routes[$requestMethod][$requestUri])) { - return $this->callRoute($this->routes[$requestMethod][$requestUri], $requestMethod === 'POST' ? $_POST : []); + $this->callRoute($this->routes[$requestMethod][$requestUri], $requestMethod === 'POST' ? ['postData' => $postData] : []); + + return; } - // If no direct match, check for dynamic routes with parameters + // Match dynamic route with parameters foreach ($this->routes[$requestMethod] as $route => $routeInfo) { - $routePattern = preg_replace('/:\w+/', '(\w+)', $route); - $pattern = '#^' . $routePattern . '$#'; - - if (preg_match($pattern, $requestUri, $matches)) { - array_shift($matches); // Remove the full match + $routePattern = preg_replace('/:\w+/', '([^/]+)', $route); + if (preg_match("#^{$routePattern}$#", $requestUri, $matches)) { + array_shift($matches); $params = $this->extractParams($route, $matches); - return $this->callRoute($routeInfo, $params, $requestMethod === 'POST' ? $_POST : []); + $arguments = $requestMethod === 'POST' ? array_merge($params, ['postData' => $postData]) : $params; + + $this->callRoute($routeInfo, $arguments); + + return; } } - echo "404 Not Found"; + $this->abort(404, 'Not Found'); + } + + public function redirect(string $uri, int $statusCode = 302): void + { + http_response_code($statusCode); + header("Location: {$uri}"); + exit; } - protected function extractParams($route, $matches) + protected function extractParams(string $route, array $matches): array { preg_match_all('/:(\w+)/', $route, $paramNames); + return array_combine($paramNames[1], $matches); } - protected function callRoute($routeInfo, $params = [], $postData = []) + protected function callRoute(array $routeInfo, array $arguments = []): void + { + if (! class_exists($routeInfo['controller'])) { + $this->abort(500, "Controller {$routeInfo['controller']} not found"); + + return; + } + + if (! method_exists($routeInfo['controller'], $routeInfo['method'])) { + $this->abort(500, "Method {$routeInfo['method']} not found in controller {$routeInfo['controller']}"); + + return; + } + + if (isset($routeInfo['middleware'])) { + $this->handleMiddleware($routeInfo['middleware']); + } + + $controller = new $routeInfo['controller']; + $controller->{$routeInfo['method']}(...$arguments); + } + + protected function handleMiddleware(array $middlewares): void { - $controller = new $routeInfo['controller'](); - $method = $routeInfo['method']; + foreach ($middlewares as $middlewareClass) { + if (! class_exists($middlewareClass)) { + throw new Exception("Middleware class {$middlewareClass} not found"); + } - // Combine route parameters and POST data - $arguments = array_merge(array_values($params), [$postData]); + $middleware = new $middlewareClass; + if (! $middleware instanceof MiddlewareInterface) { + throw new Exception("Middleware {$middlewareClass} must implement MiddlewareInterface"); + } - // Unpack the combined array into the method call - $controller->$method(...$arguments); + $middleware->handle($_REQUEST, fn () => null); + } + } + + protected function abort(int $statusCode, string $message): void + { + http_response_code($statusCode); + echo json_encode(['error' => $message]); + exit; } } diff --git a/src/app/Core/Session.php b/src/app/Core/Session.php index 3fd9100b..bc015264 100755 --- a/src/app/Core/Session.php +++ b/src/app/Core/Session.php @@ -6,17 +6,12 @@ class Session { public static function start() { - if (session_status() === PHP_SESSION_NONE) { - session_set_cookie_params([ - 'lifetime' => 0, - 'path' => '/', - 'domain' => $_SERVER['HTTP_HOST'], - 'secure' => isset($_SERVER['HTTPS']), - 'httponly' => true, - 'samesite' => 'Strict' - ]); - session_start(); - } + session_start(); + } + + public static function has($key) + { + return isset($_SESSION[$key]); } public static function set($key, $value) diff --git a/src/app/Core/View.php b/src/app/Core/View.php index 4f4d04bd..0f8ec4d6 100755 --- a/src/app/Core/View.php +++ b/src/app/Core/View.php @@ -6,22 +6,22 @@ class View { public static function render($options = []) { - $title = $options['title'] ?? "Default Title"; - $layout = $options['layout'] ?? "MainLayout"; - $view = $options['view'] ?? "Home"; + $title = $options['title'] ?? 'Default Title'; + $layout = $options['layout'] ?? 'MainLayout'; + $view = $options['view'] ?? 'Home'; $data = $options['data'] ?? []; - if (!file_exists(__DIR__ . "/../Views/{$view}.php")) { + if (! file_exists(__DIR__."/../Views/{$view}.php")) { throw new \Exception("View file not found: {$view}"); } extract($data); ob_start(); - require_once __DIR__ . "/../Views/{$view}.php"; + require_once __DIR__."/../Views/{$view}.php"; $content = ob_get_clean(); - if (file_exists(__DIR__ . "/../Layouts/{$layout}.php")) { - require_once __DIR__ . "/../Layouts/{$layout}.php"; + if (file_exists(__DIR__."/../Layouts/{$layout}.php")) { + require_once __DIR__."/../Layouts/{$layout}.php"; } else { throw new \Exception("Layout file not found: {$layout}"); } diff --git a/src/app/Layouts/AuthLayout.php b/src/app/Layouts/AuthLayout.php index 9ff89ef6..a9fd08bc 100644 --- a/src/app/Layouts/AuthLayout.php +++ b/src/app/Layouts/AuthLayout.php @@ -4,24 +4,28 @@ - <?php echo $title . ' - ' . getenv('APP_NAME'); ?> + + <?php echo htmlspecialchars($title).' - '.htmlspecialchars(getenv('APP_NAME')); ?> + -
-
-

+
+ +
+

+

- +
- +
- \ No newline at end of file + diff --git a/src/app/Layouts/MainLayout.php b/src/app/Layouts/MainLayout.php index 38ae4e11..fee21822 100755 --- a/src/app/Layouts/MainLayout.php +++ b/src/app/Layouts/MainLayout.php @@ -4,7 +4,9 @@ - <?php echo $title . ' - ' . getenv('APP_NAME'); ?> + + <?php echo $title . ' - ' . getenv('APP_NAME'); ?> + @@ -17,30 +19,107 @@
+
-
+
+
- Welcome, User - + Welcome, + + + Logout
-
- +
+
+ + + - \ No newline at end of file + diff --git a/src/app/Middlewares/AuthMiddleware.php b/src/app/Middlewares/AuthMiddleware.php new file mode 100644 index 00000000..702636ed --- /dev/null +++ b/src/app/Middlewares/AuthMiddleware.php @@ -0,0 +1,18 @@ +{$foreignKey}; - $query = "SELECT * FROM $relatedTable WHERE $ownerKey = :foreignKeyValue LIMIT 1"; + if ($foreignKeyValue === null) + return null; + + $query = "SELECT * FROM {$relatedTable} WHERE {$ownerKey} = :foreignKeyValue LIMIT 1"; $results = Database::prepareAndExecute($query, ['foreignKeyValue' => $foreignKeyValue]); - return !empty($results) ? $relatedModel::mapDataToModel($results[0]) : null; + return ! empty($results) ? $relatedModel::mapDataToModel($results[0]) : null; } // Many-to-Many relationship - public function belongsToMany($relatedModel, $pivotTable, $foreignKey, $relatedKey, $ownerKey = 'id', $relatedOwnerKey = 'id') - { + public function belongsToMany( + string $relatedModel, + string $pivotTable, + string $foreignKey, + string $relatedKey, + string $ownerKey = 'id', + string $relatedOwnerKey = 'id' + ): array { $relatedTable = $relatedModel::getTableName(); $localKeyValue = $this->{$ownerKey}; $query = " - SELECT $relatedTable.* - FROM $relatedTable - INNER JOIN $pivotTable ON $pivotTable.$relatedKey = $relatedTable.$relatedOwnerKey - WHERE $pivotTable.$foreignKey = :localKeyValue + SELECT {$relatedTable}.* + FROM {$relatedTable} + INNER JOIN {$pivotTable} ON {$pivotTable}.{$relatedKey} = {$relatedTable}.{$relatedOwnerKey} + WHERE {$pivotTable}.{$foreignKey} = :localKeyValue "; $results = Database::prepareAndExecute($query, ['localKeyValue' => $localKeyValue]); + if (! is_array($results)) + $results = []; + return array_map(fn($row) => $relatedModel::mapDataToModel($row), $results); } @@ -47,10 +58,10 @@ public function delete(): void $table = static::getTableName(); if (static::hasSoftDelete()) { - $query = "UPDATE $table SET deleted_at = NOW() WHERE id = :id"; + $query = "UPDATE {$table} SET deleted_at = NOW() WHERE id = :id"; Database::prepareAndExecute($query, ['id' => $this->id]); } else { - $query = "DELETE FROM $table WHERE id = :id"; + $query = "DELETE FROM {$table} WHERE id = :id"; Database::prepareAndExecute($query, ['id' => $this->id]); } } @@ -58,10 +69,16 @@ public function delete(): void public static function find($id) { $table = static::getTableName(); - $query = "SELECT * FROM $table WHERE id = :id LIMIT 1"; + + // Check if the table supports soft deletes + $query = "SELECT * FROM {$table} WHERE id = :id"; + if (static::hasSoftDelete()) + $query .= ' AND deleted_at IS NULL'; // Only fetch records that are not soft deleted + + $query .= ' LIMIT 1'; $results = Database::prepareAndExecute($query, ['id' => $id]); - if (!empty($results)) + if (! empty($results)) return static::mapDataToModel($results[0]); return null; @@ -70,46 +87,87 @@ public static function find($id) public static function findAll($conditions = []) { $table = static::getTableName(); - $query = "SELECT * FROM $table"; + $query = "SELECT * FROM {$table}"; $params = []; - if (!empty($conditions)) { + // Add conditions for WHERE clause + if (! empty($conditions)) { $clauses = []; foreach ($conditions as $key => $value) { - $clauses[] = "$key = :$key"; + $clauses[] = "{$key} = :{$key}"; $params[$key] = $value; } - $query .= " WHERE " . implode(" AND ", $clauses); + $query .= ' WHERE '.implode(' AND ', $clauses); + } + + // Check if the table supports soft deletes and exclude soft-deleted records + if (static::hasSoftDelete()) { + $query .= empty($conditions) ? ' WHERE' : ' AND'; + $query .= ' deleted_at IS NULL'; // Exclude soft-deleted records by default } $results = Database::prepareAndExecute($query, $params); - return array_map(fn($row) => static::mapDataToModel($row), $results); + return array_map(fn ($row) => static::mapDataToModel($row), $results); + } + + public static function findBy($conditions, $single = false) + { + $table = static::getTableName(); + + // Build the WHERE clause dynamically + $whereClauses = []; + $parameters = []; + foreach ($conditions as $column => $value) { + $whereClauses[] = "{$column} = :{$column}"; + $parameters[$column] = $value; + } + $whereClause = implode(' AND ', $whereClauses); + + // Construct the SQL query + $query = "SELECT * FROM {$table} WHERE {$whereClause}"; + + if ($single) + $query .= ' LIMIT 1'; + + $results = Database::prepareAndExecute($query, $parameters); + + if ($single) + return ! empty($results) ? static::mapDataToModel($results[0]) : null; + + if (empty($results)) + return []; + + return array_map(fn ($row) => static::mapDataToModel($row), $results); } // Fetch all soft deleted records public static function findSoftDeleted() { - if (!static::hasSoftDelete()) + if (! static::hasSoftDelete()) return []; $table = static::getTableName(); - $query = "SELECT * FROM $table WHERE deleted_at IS NOT NULL"; + $query = "SELECT * FROM {$table} WHERE deleted_at IS NOT NULL"; $results = Database::prepareAndExecute($query); return array_map(fn($row) => static::mapDataToModel($row), $results); } // Dynamically relationship fetching - public function hasMany($relatedModel, $foreignKey, $localKey = 'id') + public function hasMany(string $relatedModel, string $foreignKey, string $localKey = 'id'): array { $relatedTable = $relatedModel::getTableName(); $localKeyValue = $this->{$localKey}; - $query = "SELECT * FROM $relatedTable WHERE $foreignKey = :localKeyValue"; + $query = "SELECT * FROM {$relatedTable} WHERE {$foreignKey} = :localKeyValue"; $results = Database::prepareAndExecute($query, ['localKeyValue' => $localKeyValue]); - return array_map(fn($row) => $relatedModel::mapDataToModel($row), $results); + // Ensure $results is an array + if (! is_array($results)) + $results = []; + + return array_map(fn ($row) => $relatedModel::mapDataToModel($row), $results); } // Dynamically check if a table has the deleted_at column @@ -118,10 +176,10 @@ protected static function hasSoftDelete(): bool static $softDeleteCache = []; $table = static::getTableName(); - if (!isset($softDeleteCache[$table])) { - $query = "SHOW COLUMNS FROM $table LIKE 'deleted_at'"; + if (! isset($softDeleteCache[$table])) { + $query = "SHOW COLUMNS FROM {$table} LIKE 'deleted_at'"; $result = Database::prepareAndExecute($query); - $softDeleteCache[$table] = !empty($result); // Cache the result + $softDeleteCache[$table] = ! empty($result); // Cache the result } return $softDeleteCache[$table]; @@ -131,7 +189,7 @@ public function restore(): void { if (static::hasSoftDelete()) { $table = static::getTableName(); - $query = "UPDATE $table SET deleted_at = NULL WHERE id = :id"; + $query = "UPDATE {$table} SET deleted_at = NULL WHERE id = :id"; Database::prepareAndExecute($query, ['id' => $this->id]); } } @@ -146,20 +204,20 @@ public function save(): void // Update logic $fields = []; foreach ($properties as $key => $value) { - $fields[] = "$key = :$key"; + $fields[] = "{$key} = :{$key}"; } - $query = "UPDATE $table SET " . implode(", ", $fields) . " WHERE id = :id"; + $query = "UPDATE {$table} SET ".implode(", ", $fields)." WHERE id = :id"; $properties['id'] = $this->id; } else { // Insert logic $fields = array_keys($properties); - $placeholders = array_map(fn($field) => ":$field", $fields); - $query = "INSERT INTO $table (" . implode(", ", $fields) . ") VALUES (" . implode(", ", $placeholders) . ")"; + $placeholders = array_map(fn($field) => ":{$field}", $fields); + $query = "INSERT INTO {$table} (".implode(", ", $fields).") VALUES (".implode(", ", $placeholders).")"; } Database::prepareAndExecute($query, $properties); - if (!$this->id) + if (! $this->id) $this->id = Database::connect()->lastInsertId(); } diff --git a/src/app/Models/MContract.php b/src/app/Models/Contract.php similarity index 88% rename from src/app/Models/MContract.php rename to src/app/Models/Contract.php index 47145a0c..e0fdd023 100644 --- a/src/app/Models/MContract.php +++ b/src/app/Models/Contract.php @@ -2,13 +2,18 @@ namespace App\Models; -class MContract extends BaseModel +class Contract extends BaseModel { public string $name; + public string $start_date; + public ?string $end_date; + public ?float $invoice_proposed; + public ?float $invoice_agreed; + public ?float $invoice_paid; protected static function getTableName(): string @@ -16,7 +21,7 @@ protected static function getTableName(): string return 'contracts'; } - protected static function mapDataToModel($data): MContract + protected static function mapDataToModel($data): Contract { $contract = new self(); $contract->id = $data['id']; @@ -27,6 +32,7 @@ protected static function mapDataToModel($data): MContract $contract->invoice_agreed = $data['invoice_agreed']; $contract->invoice_paid = $data['invoice_paid']; $contract->created_at = $data['created_at']; + return $contract; } } diff --git a/src/app/Models/MElement.php b/src/app/Models/Element.php similarity index 55% rename from src/app/Models/MElement.php rename to src/app/Models/Element.php index 6a698168..cefcf495 100644 --- a/src/app/Models/MElement.php +++ b/src/app/Models/Element.php @@ -2,19 +2,22 @@ namespace App\Models; -class MElement extends BaseModel +class Element extends BaseModel { public string $name; + public int $zone_id; + public int $point_id; + public int $tree_type_id; - protected static function getTableName(): string + protected static function getTableName() { return 'elements'; } - protected static function mapDataToModel($data): MElement + protected static function mapDataToModel($data): Element { $element = new self(); $element->id = $data['id']; @@ -23,21 +26,22 @@ protected static function mapDataToModel($data): MElement $element->point_id = $data['point_id']; $element->tree_type_id = $data['tree_type_id']; $element->created_at = $data['created_at']; + return $element; } - public function zone(): MZone + public function zone() { - return $this->belongsTo(MZone::class, 'zone_id'); + return $this->belongsTo(Zone::class, 'zone_id'); } - public function point(): MPoint + public function point() { - return $this->belongsTo(MPoint::class, 'point_id'); + return $this->belongsTo(Point::class, 'point_id'); } - public function treeType(): MTreeType + public function treeType() { - return $this->belongsTo(MTreeType::class, 'tree_type_id'); + return $this->belongsTo(TreeType::class, 'tree_type_id'); } } diff --git a/src/app/Models/MIncidence.php b/src/app/Models/Incidence.php similarity index 76% rename from src/app/Models/MIncidence.php rename to src/app/Models/Incidence.php index ef0ea729..36ac0b18 100644 --- a/src/app/Models/MIncidence.php +++ b/src/app/Models/Incidence.php @@ -2,11 +2,14 @@ namespace App\Models; -class MIncidence extends BaseModel +class Incidence extends BaseModel { public int $element_id; + public string $name; + public ?string $description; + public ?string $photo; protected static function getTableName(): string @@ -14,7 +17,7 @@ protected static function getTableName(): string return 'incidences'; } - public static function mapDataToModel($data): MIncidence + public static function mapDataToModel($data): Incidence { $incidence = new self(); $incidence->id = $data['id']; @@ -23,12 +26,13 @@ public static function mapDataToModel($data): MIncidence $incidence->description = $data['description']; $incidence->photo = $data['photo']; $incidence->created_at = $data['created_at']; + return $incidence; } // Fetch the element of the incidence - public function element(): MElement + public function element(): Element { - return $this->belongsTo(MElement::class, 'element_id'); + return $this->belongsTo(Element::class, 'element_id'); } } diff --git a/src/app/Models/MMachine.php b/src/app/Models/Machine.php similarity index 82% rename from src/app/Models/MMachine.php rename to src/app/Models/Machine.php index 9e3c6673..c04fa6dc 100644 --- a/src/app/Models/MMachine.php +++ b/src/app/Models/Machine.php @@ -2,7 +2,7 @@ namespace App\Models; -class MMachine extends BaseModel +class Machine extends BaseModel { public ?string $name; public float $max_basket_size; @@ -12,13 +12,14 @@ protected static function getTableName(): string return 'machines'; } - protected static function mapDataToModel($data): MMachine + protected static function mapDataToModel($data): Machine { $machine = new self(); $machine->id = $data['id']; $machine->name = $data['name']; $machine->max_basket_size = $data['max_basket_size']; $machine->created_at = $data['created_at']; + return $machine; } } diff --git a/src/app/Models/MPoint.php b/src/app/Models/Point.php similarity index 81% rename from src/app/Models/MPoint.php rename to src/app/Models/Point.php index c4cd7a84..f434b472 100644 --- a/src/app/Models/MPoint.php +++ b/src/app/Models/Point.php @@ -2,9 +2,10 @@ namespace App\Models; -class MPoint extends BaseModel +class Point extends BaseModel { public $latitude; + public $longitude; protected static function getTableName(): string @@ -12,13 +13,14 @@ protected static function getTableName(): string return 'points'; } - protected static function mapDataToModel($data): MPoint + protected static function mapDataToModel($data): Point { $point = new self(); $point->id = $data['id']; $point->latitude = $data['latitude']; $point->longitude = $data['longitude']; $point->created_at = $data['created_at']; + return $point; } } diff --git a/src/app/Models/MPruningType.php b/src/app/Models/PruningType.php similarity index 82% rename from src/app/Models/MPruningType.php rename to src/app/Models/PruningType.php index 51752c1b..24d0468a 100644 --- a/src/app/Models/MPruningType.php +++ b/src/app/Models/PruningType.php @@ -2,9 +2,10 @@ namespace App\Models; -class MPruningType extends BaseModel +class PruningType extends BaseModel { public string $name; + public ?string $description; protected static function getTableName(): string @@ -12,13 +13,14 @@ protected static function getTableName(): string return 'pruning_types'; } - protected static function mapDataToModel($data): MPruningType + protected static function mapDataToModel($data): PruningType { $pruning_type = new self(); $pruning_type->id = $data['id']; $pruning_type->name = $data['name']; $pruning_type->description = $data['description']; $pruning_type->created_at = $data['created_at']; + return $pruning_type; } } diff --git a/src/app/Models/MRole.php b/src/app/Models/Role.php similarity index 78% rename from src/app/Models/MRole.php rename to src/app/Models/Role.php index 6d3592bf..016ece72 100644 --- a/src/app/Models/MRole.php +++ b/src/app/Models/Role.php @@ -2,7 +2,7 @@ namespace App\Models; -class MRole extends BaseModel +class Role extends BaseModel { public string $name; @@ -11,12 +11,13 @@ protected static function getTableName(): string return 'roles'; } - protected static function mapDataToModel($data): MRole + protected static function mapDataToModel($data): Role { $role = new self(); $role->id = $data['id']; $role->name = $data['name']; $role->created_at = $data['created_at']; + return $role; } } diff --git a/src/app/Models/MRoute.php b/src/app/Models/Route.php similarity index 73% rename from src/app/Models/MRoute.php rename to src/app/Models/Route.php index 7acbce2e..6cd17bee 100644 --- a/src/app/Models/MRoute.php +++ b/src/app/Models/Route.php @@ -2,7 +2,7 @@ namespace App\Models; -class MRoute extends BaseModel +class Route extends BaseModel { public ?float $distance; public ?int $point_id; @@ -13,7 +13,7 @@ protected static function getTableName(): string return 'routes'; } - protected static function mapDataToModel($data): MRoute + protected static function mapDataToModel($data): Route { $role = new self(); $role->id = $data['id']; @@ -21,11 +21,12 @@ protected static function mapDataToModel($data): MRoute $role->point_id = $data['point_id']; $role->travel_time = $data['travel_time']; $role->created_at = $data['created_at']; + return $role; } - public function point(): MPoint + public function point(): Point { - return $this->belongsTo(MPoint::class, 'point_id'); + return $this->belongsTo(Point::class, 'point_id'); } } diff --git a/src/app/Models/MSensor.php b/src/app/Models/Sensor.php similarity index 73% rename from src/app/Models/MSensor.php rename to src/app/Models/Sensor.php index 767b93cd..8929281d 100644 --- a/src/app/Models/MSensor.php +++ b/src/app/Models/Sensor.php @@ -2,7 +2,7 @@ namespace App\Models; -class MSensor extends BaseModel +class Sensor extends BaseModel { public int $zone_id; public ?string $model; @@ -14,7 +14,7 @@ protected static function getTableName(): string return "sensors"; } - protected static function mapDataToModel($data): MSensor + protected static function mapDataToModel($data): Sensor { $sensor = new self(); $sensor->zone_id = $data["zone_id"]; @@ -22,11 +22,12 @@ protected static function mapDataToModel($data): MSensor $sensor->class = $data["class"]; $sensor->is_active = $data["is_active"]; $sensor->created_at = $data["created_at"]; + return $sensor; } - public function zone(): MZone + public function zone(): Zone { - return $this->belongsTo(MZone::class, "zone_id", "id"); + return $this->belongsTo(Zone::class, "zone_id", "id"); } } diff --git a/src/app/Models/MSensorHistory.php b/src/app/Models/SensorHistory.php similarity index 75% rename from src/app/Models/MSensorHistory.php rename to src/app/Models/SensorHistory.php index b469efa5..0b1e1d47 100644 --- a/src/app/Models/MSensorHistory.php +++ b/src/app/Models/SensorHistory.php @@ -2,7 +2,7 @@ namespace App\Models; -class MSensorHistory extends BaseModel +class SensorHistory extends BaseModel { public int $sensor_id; public ?float $temperature; @@ -14,7 +14,7 @@ protected static function getTableName(): string return 'sensor_history'; } - protected static function mapDataToModel($data): MSensorHistory + protected static function mapDataToModel($data): SensorHistory { $sensor_history = new self(); $sensor_history->sensor_id = $data['sensor_id']; @@ -22,11 +22,12 @@ protected static function mapDataToModel($data): MSensorHistory $sensor_history->humidity = $data['humidity']; $sensor_history->inclination = $data['inclination']; $sensor_history->created_at = $data['created_at']; + return $sensor_history; } - public function sensor(): MSensor + public function sensor(): Sensor { - return $this->belongsTo(MSensor::class, 'sensor_id'); + return $this->belongsTo(Sensor::class, 'sensor_id'); } } diff --git a/src/app/Models/MTask.php b/src/app/Models/Task.php similarity index 54% rename from src/app/Models/MTask.php rename to src/app/Models/Task.php index 62112c97..f0ee4f61 100644 --- a/src/app/Models/MTask.php +++ b/src/app/Models/Task.php @@ -4,7 +4,7 @@ use App\Models\MWorkOrder; -class MTask extends BaseModel +class Task extends BaseModel { public int $work_order_id; public ?string $notes; @@ -14,33 +14,34 @@ protected static function getTableName(): string return 'tasks'; } - protected static function mapDataToModel($data): MTask + protected static function mapDataToModel($data): Task { $task = new self(); $task->id = $data['id']; $task->notes = $data['notes']; $task->work_order_id = $data['work_order_id']; $task->created_at = $data['created_at']; + return $task; } - public function order(): MWorkOrder + public function order(): WorkOrder { - return $this->belongsTo(MWorkOrder::class, 'work_order_id', 'id'); + return $this->belongsTo(WorkOrder::class, 'work_order_id', 'id'); } public function zones() { - return $this->belongsToMany(MZone::class, 'tasks_zones', 'task_id', 'zone_id'); + return $this->belongsToMany(Zone::class, 'tasks_zones', 'task_id', 'zone_id'); } public function workers() { - return $this->belongsToMany(MWorker::class, 'tasks_workers', 'task_id', 'worker_id'); + return $this->belongsToMany(User::class, 'tasks_workers', 'task_id', 'worker_id'); } public function taskTypes() { - return $this->belongsToMany(MTaskType::class, 'tasks_tasktypes', 'task_id', 'tasktype_id'); + return $this->belongsToMany(TaskType::class, 'tasks_tasktypes', 'task_id', 'tasktype_id'); } } diff --git a/src/app/Models/MTaskType.php b/src/app/Models/TaskType.php similarity index 77% rename from src/app/Models/MTaskType.php rename to src/app/Models/TaskType.php index f85586d4..284828f1 100644 --- a/src/app/Models/MTaskType.php +++ b/src/app/Models/TaskType.php @@ -2,7 +2,7 @@ namespace App\Models; -class MTaskType extends BaseModel +class TaskType extends BaseModel { public string $name; @@ -11,12 +11,13 @@ protected static function getTableName(): string return 'task_types'; } - protected static function mapDataToModel($data): MTaskType + protected static function mapDataToModel($data): TaskType { $task = new self(); $task->id = $data['id']; $task->name = $data['name']; $task->created_at = $data['created_at']; + return $task; } } diff --git a/src/app/Models/MTreeType.php b/src/app/Models/TreeType.php similarity index 83% rename from src/app/Models/MTreeType.php rename to src/app/Models/TreeType.php index ea81aa43..e14a4e31 100644 --- a/src/app/Models/MTreeType.php +++ b/src/app/Models/TreeType.php @@ -2,10 +2,12 @@ namespace App\Models; -class MTreeType extends BaseModel +class TreeType extends BaseModel { public string $family; + public string $genus; + public string $species; protected static function getTableName(): string @@ -13,7 +15,7 @@ protected static function getTableName(): string return 'tree_types'; } - protected static function mapDataToModel($data): MTreeType + protected static function mapDataToModel($data): TreeType { $tree_type = new self(); $tree_type->id = $data['id']; @@ -21,6 +23,7 @@ protected static function mapDataToModel($data): MTreeType $tree_type->genus = $data['genus']; $tree_type->species = $data['species']; $tree_type->created_at = $data['created_at']; + return $tree_type; } } diff --git a/src/app/Models/MWorker.php b/src/app/Models/User.php similarity index 61% rename from src/app/Models/MWorker.php rename to src/app/Models/User.php index 75aefb6e..fa2d6e3c 100644 --- a/src/app/Models/MWorker.php +++ b/src/app/Models/User.php @@ -2,22 +2,26 @@ namespace App\Models; -class MWorker extends BaseModel +class User extends BaseModel { + public string $company; + + public string $name; - public ?string $company; - public ?string $name; public string $dni; - public ?string $password; - public ?string $email; - public ?int $role_id; + + public string $password; + + public string $email; + + public int $role_id; protected static function getTableName(): string { return 'workers'; } - protected static function mapDataToModel($data): MWorker + protected static function mapDataToModel($data): User { $user = new self(); $user->id = $data['id']; @@ -28,11 +32,13 @@ protected static function mapDataToModel($data): MWorker $user->email = $data['email']; $user->role_id = $data['role_id']; $user->created_at = $data['created_at']; + return $user; } - public function role(): MRole + // Fetch the user's role + public function role(): Role { - return $this->belongsTo(MRole::class, 'role_id'); + return $this->belongsTo(Role::class, 'role_id'); } } diff --git a/src/app/Models/MWorkOrder.php b/src/app/Models/WorkOrder.php similarity index 52% rename from src/app/Models/MWorkOrder.php rename to src/app/Models/WorkOrder.php index dd27e245..04489af8 100644 --- a/src/app/Models/MWorkOrder.php +++ b/src/app/Models/WorkOrder.php @@ -2,7 +2,7 @@ namespace App\Models; -class MWorkOrder extends BaseModel +class WorkOrder extends BaseModel { public ?int $contract_id; @@ -11,27 +11,28 @@ protected static function getTableName(): string return 'work_orders'; } - protected static function mapDataToModel($data): MWorkOrder + protected static function mapDataToModel($data): WorkOrder { $order = new self(); $order->id = $data['id']; $order->contract_id = $data['contract_id']; $order->created_at = $data['created_at']; + return $order; } - public function report(): MWorkReport + public function report(): WorkReport { - return $this->hasMany(MWorkReport::class, 'id')[0]; + return $this->hasMany(WorkReport::class, 'id')[0]; } - public function contract(): MContract + public function contract(): Contract { - return $this->belongsTo(MContract::class, 'contract_id', 'id'); + return $this->belongsTo(Contract::class, 'contract_id', 'id'); } public function tasks() { - return $this->hasMany(MTask::class, 'work_order_id', 'id'); + return $this->hasMany(Task::class, 'work_order_id', 'id'); } } diff --git a/src/app/Models/MWorkReport.php b/src/app/Models/WorkReport.php similarity index 73% rename from src/app/Models/MWorkReport.php rename to src/app/Models/WorkReport.php index 49085bf2..1a557af1 100644 --- a/src/app/Models/MWorkReport.php +++ b/src/app/Models/WorkReport.php @@ -2,7 +2,7 @@ namespace App\Models; -class MWorkReport extends BaseModel +class WorkReport extends BaseModel { public ?string $observation; public ?float $spent_fuel; @@ -13,7 +13,7 @@ protected static function getTableName(): string return 'work_reports'; } - protected static function mapDataToModel($data): MWorkReport + protected static function mapDataToModel($data): WorkReport { $workReport = new self(); $workReport->id = $data['id']; @@ -21,11 +21,12 @@ protected static function mapDataToModel($data): MWorkReport $workReport->spent_fuel = $data['spent_fuel']; $workReport->photo = $data['photo']; $workReport->created_at = $data['created_at']; + return $workReport; } - public function workOrder(): MWorkOrder + public function workOrder(): WorkOrder { - return $this->belongsTo(MWorkOrder::class, 'id'); + return $this->belongsTo(WorkOrder::class, 'id'); } } diff --git a/src/app/Models/MZone.php b/src/app/Models/Zone.php similarity index 70% rename from src/app/Models/MZone.php rename to src/app/Models/Zone.php index 31f76af6..65603c6a 100644 --- a/src/app/Models/MZone.php +++ b/src/app/Models/Zone.php @@ -2,10 +2,12 @@ namespace App\Models; -class MZone extends BaseModel +class Zone extends BaseModel { public string $name; + public int $postal_code; + public int $point_id; protected static function getTableName(): string @@ -13,7 +15,7 @@ protected static function getTableName(): string return 'zones'; } - protected static function mapDataToModel($data): MZone + protected static function mapDataToModel($data): Zone { $zone = new self(); $zone->id = $data['id']; @@ -21,11 +23,13 @@ protected static function mapDataToModel($data): MZone $zone->postal_code = $data['postal_code']; $zone->point_id = $data['point_id']; $zone->created_at = $data['created_at']; + return $zone; } - public function point(): MPoint + // Fetch the zone's point + public function point() { - return $this->belongsTo(MPoint::class, 'point_id'); + return $this->belongsTo(Point::class, 'point_id'); } } diff --git a/src/app/Views/Auth/Login.php b/src/app/Views/Auth/Login.php index 7128e4ff..1a4605ec 100644 --- a/src/app/Views/Auth/Login.php +++ b/src/app/Views/Auth/Login.php @@ -1,30 +1,44 @@ - + +?> + + + + + + + +
- +
- +
- - -
- -
- Forgot your password? + +
-
-
\ No newline at end of file + diff --git a/src/app/Views/Contracts.php b/src/app/Views/Contracts.php deleted file mode 100644 index f317278f..00000000 --- a/src/app/Views/Contracts.php +++ /dev/null @@ -1,30 +0,0 @@ -
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
IDNameStart DateEnd DateInvoice proposedInvoice agreedInvoice paidCreated at
getId(); ?>name; ?>start_date; ?>end_date; ?>invoice_proposed; ?>invoice_agreed; ?>invoice_paid; ?>created_at; ?>
-
\ No newline at end of file diff --git a/src/app/Views/Dashboard.php b/src/app/Views/Dashboard.php new file mode 100755 index 00000000..83b82b24 --- /dev/null +++ b/src/app/Views/Dashboard.php @@ -0,0 +1,37 @@ +
+ +
+

Dashboard

+

Welcome back, + + to your dashboard. Here's a quick overview of your data. +

+
+ + +
+ +
+ +
+

Example 1

+

0

+

+0% since last week

+
+ + +
+

Example 2

+

0

+

+0% since last week

+
+ + +
+

Example 3

+

0

+

+0% since week

+
+
+
+
diff --git a/src/app/Views/Element.php b/src/app/Views/Element.php deleted file mode 100644 index 4b5b7d0d..00000000 --- a/src/app/Views/Element.php +++ /dev/null @@ -1,30 +0,0 @@ -
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
IDNameZone NameZone Postal CodeSpeciesLatitudeLongitudeCreated At
getId(); ?>name; ?>zone()->name; ?>zone()->postal_code; ?>treeType()->species; ?>point()->latitude; ?>point()->longitude; ?>getCreatedAt(); ?>
-
\ No newline at end of file diff --git a/src/app/Views/Elements.php b/src/app/Views/Elements.php new file mode 100644 index 00000000..d4dc7a7d --- /dev/null +++ b/src/app/Views/Elements.php @@ -0,0 +1,58 @@ + + + + + + +
+ + Create Element + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
IDNameZone NameZone Postal CodeSpeciesLatitudeLongitudeCreated At
+ getId(); ?> + name; ?> + zone()->name; ?> + zone()->postal_code; ?> + treeType()->species; ?> + point()->latitude; ?> + point()->longitude; ?> + getCreatedAt(); ?>
+
diff --git a/src/app/Views/Home.php b/src/app/Views/Home.php deleted file mode 100755 index 875643ff..00000000 --- a/src/app/Views/Home.php +++ /dev/null @@ -1,26 +0,0 @@ -
- - - - - - - - - - - - - - - - - - - - - - - -
IDCompanyNameDNIEmailRole Name
getId(); ?>company; ?>name; ?>dni; ?>email; ?>role()->name; ?>
-
\ No newline at end of file diff --git a/src/app/Views/Incidence.php b/src/app/Views/Incidence.php index ddc538f8..ee410d8e 100644 --- a/src/app/Views/Incidence.php +++ b/src/app/Views/Incidence.php @@ -2,12 +2,20 @@

Incidencias

Here you can see your incidences 😁

- - +
diff --git a/src/app/Views/Incidence/Create.php b/src/app/Views/Incidence/Create.php index a47868c8..59c368bc 100644 --- a/src/app/Views/Incidence/Create.php +++ b/src/app/Views/Incidence/Create.php @@ -15,7 +15,7 @@ - +
diff --git a/src/app/Views/Incidence/SeeAllIncidences.php b/src/app/Views/Incidence/SeeAllIncidences.php index 2128fca5..be42ad97 100644 --- a/src/app/Views/Incidence/SeeAllIncidences.php +++ b/src/app/Views/Incidence/SeeAllIncidences.php @@ -3,7 +3,7 @@ - + @@ -14,7 +14,7 @@ - + diff --git a/src/app/Views/PruningType.php b/src/app/Views/PruningType.php deleted file mode 100644 index 6d9b3de5..00000000 --- a/src/app/Views/PruningType.php +++ /dev/null @@ -1,21 +0,0 @@ -
-
Element Name Name Description
name; ?> element()->name; ?> description; ?>
- - - - - - - - - - - - - - - - - -
IDNameDescription
getId(); ?>name; ?>description; ?>
-
\ No newline at end of file diff --git a/src/app/Views/TaskTypes/index.php b/src/app/Views/TaskTypes/index.php deleted file mode 100644 index fdabb7a0..00000000 --- a/src/app/Views/TaskTypes/index.php +++ /dev/null @@ -1,18 +0,0 @@ -
- - - - - - - - - - - - - - - -
IDType of Task
getId(); ?>name; ?>
-
\ No newline at end of file diff --git a/src/app/Views/TreeType.php b/src/app/Views/TreeType.php deleted file mode 100644 index 5095957a..00000000 --- a/src/app/Views/TreeType.php +++ /dev/null @@ -1,22 +0,0 @@ -
- - - - - - - - - - - - - - - - - - - -
IDFamilyGenusSpecies
getId(); ?>family; ?>genus; ?>species; ?>
-
\ No newline at end of file diff --git a/src/app/Views/TreeTypes.php b/src/app/Views/TreeTypes.php new file mode 100644 index 00000000..d5a9846a --- /dev/null +++ b/src/app/Views/TreeTypes.php @@ -0,0 +1,46 @@ + + + + + + +
+ + Create Tree Type + +
+ +
+ + + + + + + + + + + + + + + + + + + +
IDFamilyGenusSpecies
+ getId(); ?> + family; ?> + genus; ?> + species; ?>
+
diff --git a/src/app/Views/User/Create.php b/src/app/Views/User/Create.php new file mode 100644 index 00000000..0759714d --- /dev/null +++ b/src/app/Views/User/Create.php @@ -0,0 +1,53 @@ +
+ + + + + + Return to Zones + +
+ +
+

Create Zone

+
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ +
+
+
diff --git a/src/app/Views/User/Edit.php b/src/app/Views/User/Edit.php new file mode 100644 index 00000000..a305df7d --- /dev/null +++ b/src/app/Views/User/Edit.php @@ -0,0 +1,79 @@ +
+ + + + + + Return to Users + +
+ +
+

Edit User

+
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ +
+
+
diff --git a/src/app/Views/Users.php b/src/app/Views/Users.php new file mode 100755 index 00000000..5b9d0d57 --- /dev/null +++ b/src/app/Views/Users.php @@ -0,0 +1,76 @@ + + + + + + +
+ + Create User + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
IDCompanyNameDNIEmailRole NameActions
+ getId()); ?> + + company); ?> + + name); ?> + + dni); ?> + + email); ?> + + role()->name); ?> + + + + + + + + + + + + + +
+
diff --git a/src/app/Views/WorkOrders.php b/src/app/Views/WorkOrders.php new file mode 100644 index 00000000..177939b0 --- /dev/null +++ b/src/app/Views/WorkOrders.php @@ -0,0 +1,89 @@ + + + + + + +
+ + Create Work Order + +
+ +
+ + + + + + + + + + + + + + + + tasks() as $task) { ?> + + + + + + + + + + + + + + +
IDContracteDataZonesTasquesOperarisNotesAccions
+ getId(); ?> + contract()->name; ?> + getCreatedAt(); ?> + zones() as $zone) { ?> + name; ?>, + + + taskTypes() as $task_type) { ?> + name; ?>, + + + workers() as $worker) { ?> + name; ?>, + + notes; ?> + + + + + + + + + + + + + +
+
diff --git a/src/app/Views/Zone.php b/src/app/Views/Zone.php deleted file mode 100644 index fbc4e8f7..00000000 --- a/src/app/Views/Zone.php +++ /dev/null @@ -1,25 +0,0 @@ -
- - - - - - - - - - - - - - - - - - - - - - -
IDNamePostal CodeLatitudeLongitude
getId(); ?>name; ?>postal_code; ?>point()->latitude; ?>point()->longitude; ?>
-
\ No newline at end of file diff --git a/src/app/Views/Zone/Create.php b/src/app/Views/Zone/Create.php new file mode 100644 index 00000000..ff5815d8 --- /dev/null +++ b/src/app/Views/Zone/Create.php @@ -0,0 +1,46 @@ +
+ + + + + + Return to Zones + +
+ +
+

Create Zone

+
+ + +
+ + +
+ + +
+ + +
+ + +
+ +

Editor not implemented

+
+ + +
+ +
+
+
diff --git a/src/app/Views/Zone/Edit.php b/src/app/Views/Zone/Edit.php new file mode 100644 index 00000000..815e72ab --- /dev/null +++ b/src/app/Views/Zone/Edit.php @@ -0,0 +1,47 @@ +
+ + + + + + Return to Zones + +
+ +
+

Edit Zone

+
+ + +
+ + +
+ + +
+ + +
+ +
+ +

Editor not implemented

+
+ + +
+ +
+
+
diff --git a/src/app/Views/Zones.php b/src/app/Views/Zones.php new file mode 100644 index 00000000..ef4bfea7 --- /dev/null +++ b/src/app/Views/Zones.php @@ -0,0 +1,62 @@ + + + + + + +
+ + Create Zone + +
+ +
+ + + + + + + + + + + + + + + + + + + +
Zone IDNamePostal CodeActions
+ getId()); ?> + name); ?> + postal_code); ?> + + + + + + + + + + + + + +
+
diff --git a/src/app/bootstrap.php b/src/app/bootstrap.php index 3b7f102f..4afccc78 100644 --- a/src/app/bootstrap.php +++ b/src/app/bootstrap.php @@ -1,8 +1,8 @@ [ - * "URI" => [ - * "controller" => "ControllerName", - * "method" => "methodName" - * ] - * ] - * - **/ - -use App\Controllers\CHome; -use App\Controllers\CAuth; -use App\Controllers\CIncidence; - -return $routes = [ - "GET" => [ - "/" => [ - "controller" => CHome::class, - "method" => "index" - ], - "/login" => [ - "controller" => CAuth::class, - "method" => "index" - ], - "/incidence" => [ - "controller" => CIncidence::class, - "method"=> "index" - ], - "/incidence/create" => [ - "controller" => CIncidence::class, - "method"=> "get" - ], - "/incidence/all" => [ - "controller" => CIncidence::class, - "method"=> "findall" - ] - ], -]; diff --git a/src/public/index.php b/src/public/index.php index ded6b8f8..6b98b851 100755 --- a/src/public/index.php +++ b/src/public/index.php @@ -1,10 +1,10 @@ load('../app/routes.php'); -$router->dispatch($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI']); +$router = new Router; +$router->load('../routes/web.php'); +$router->dispatch($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI'], $_POST); diff --git a/src/routes/web.php b/src/routes/web.php new file mode 100644 index 00000000..0cf6f74a --- /dev/null +++ b/src/routes/web.php @@ -0,0 +1,165 @@ + [ + // == Auth GET Routes + '/auth/login' => [ + 'controller' => AuthController::class, + 'method' => 'index', + 'middleware' => [GuestMiddleware::class], + ], + '/logout' => [ + 'controller' => AuthController::class, + 'method' => 'logout', + 'middleware' => [AuthMiddleware::class], + ], + // === App GET Routes + '/' => [ + 'controller' => DashboardController::class, + 'method' => 'index', + 'middleware' => [AuthMiddleware::class], + ], + // === Users GET Routes + '/users' => [ + 'controller' => UserController::class, + 'method' => 'index', + 'middleware' => [AuthMiddleware::class], + ], + '/user/create' => [ + 'controller' => UserController::class, + 'method' => 'create', + 'middleware' => [AuthMiddleware::class], + ], + '/user/:id/edit' => [ + 'controller' => UserController::class, + 'method' => 'edit', + 'middleware' => [AuthMiddleware::class], + ], + '/user/:id/delete' => [ + 'controller' => UserController::class, + 'method' => 'destroy', + 'middleware' => [AuthMiddleware::class], + ], + // === WorkOrders GET Routes + '/work-orders' => [ + 'controller' => WorkOrderController::class, + 'method' => 'index', + 'middleware' => [AuthMiddleware::class], + ], + '/work-order/create' => [ + 'controller' => WorkOrderController::class, + 'method' => 'create', + 'middleware' => [AuthMiddleware::class], + ], + '/work-order/:id/edit' => [ + 'controller' => WorkOrderController::class, + 'method' => 'edit', + 'middleware' => [AuthMiddleware::class], + ], + '/work-order/:id/delete' => [ + 'controller' => WorkOrderController::class, + 'method' => 'destroy', + 'middleware' => [AuthMiddleware::class], + ], + // === Zones GET Routes + '/zones' => [ + 'controller' => ZoneController::class, + 'method' => 'index', + 'middleware' => [AuthMiddleware::class], + ], + '/zone/create' => [ + 'controller' => ZoneController::class, + 'method' => 'create', + 'middleware' => [AuthMiddleware::class], + ], + '/zone/:id/edit' => [ + 'controller' => ZoneController::class, + 'method' => 'edit', + 'middleware' => [AuthMiddleware::class], + ], + '/zone/:id/delete' => [ + 'controller' => ZoneController::class, + 'method' => 'destroy', + 'middleware' => [AuthMiddleware::class], + ], + // === TreeTypes GET Routes + '/tree-types' => [ + 'controller' => TreeTypeController::class, + 'method' => 'index', + 'middleware' => [AuthMiddleware::class], + ], + // === Incidence GET Routes + '/incidence' => [ + 'controller' => IncidenceController::class, + 'method' => 'index', + ], + '/incidence/create' => [ + 'controller' => IncidenceController::class, + 'method' => 'get', + ], + '/incidence/all' => [ + 'controller' => IncidenceController::class, + 'method' => 'findall', + ], + // === Elements GET Routes + '/elements' => [ + 'controller' => ElementController::class, + 'method' => 'index', + 'middleware' => [AuthMiddleware::class], + ], + ], + 'POST' => [ + // == Auth POST Routes + '/auth/login' => [ + 'controller' => AuthController::class, + 'method' => 'login', + 'middleware' => [GuestMiddleware::class], + ], + '/user/store' => [ + 'controller' => UserController::class, + 'method' => 'store', + 'middleware' => [AuthMiddleware::class], + ], + '/user/:id/update' => [ + 'controller' => UserController::class, + 'method' => 'update', + 'middleware' => [AuthMiddleware::class], + ], + // === WorkOrders POST Routes + '/work-order/store' => [ + 'controller' => WorkOrderController::class, + 'method' => 'store', + 'middleware' => [AuthMiddleware::class], + ], + '/work-order/:id/update' => [ + 'controller' => WorkOrderController::class, + 'method' => 'update', + 'middleware' => [AuthMiddleware::class], + ], + // === Zones POST Routes + '/zone/store' => [ + 'controller' => ZoneController::class, + 'method' => 'store', + 'middleware' => [AuthMiddleware::class], + ], + '/zone/:id/update' => [ + 'controller' => ZoneController::class, + 'method' => 'update', + 'middleware' => [AuthMiddleware::class], + ], + ], +]; diff --git a/src/storage/logs/app.log b/src/storage/logs/app.log old mode 100644 new mode 100755