From 405ef421c78babe68df974f96210ce921b802558 Mon Sep 17 00:00:00 2001 From: Dyze Date: Sat, 26 Oct 2024 19:47:18 +0200 Subject: [PATCH] Error management and cart --- public/index.php | 19 +++- src/Controllers/Account/AccountController.php | 6 ++ src/Controllers/BaseController.php | 12 ++- .../Manage/ManageDVDController.php | 3 +- src/Controllers/Order/NewOrderController.php | 48 ++++++++++ src/Controllers/Search/SearchController.php | 38 ++++++++ src/Middlewares/Interceptor.php | 10 +- src/Middlewares/Routing.php | 13 ++- src/Models/Exceptions/BadRouteException.php | 14 --- src/Models/Exceptions/CustomException.php | 14 +++ src/Models/Exceptions/RouteException.php | 14 --- .../Exceptions/RouteNotFoundException.php | 12 --- src/Models/ViewModels/NewOrderViewModel.php | 21 +++++ src/Models/ViewModels/SearchViewModel.php | 4 +- src/Services/DataService.php | 19 ++-- src/Utils/JWTUtils.php | 7 +- src/Views/BaseView.php | 4 +- src/Views/DVD/DVDView.style.css | 15 ++- src/Views/DVD/DVDView.template.php | 18 ++-- src/Views/Home/HomeView.js | 11 ++- src/Views/Home/HomeView.template.php | 2 +- src/Views/Layout/Components/Cart.template.php | 14 ++- src/Views/Layout/LayoutView.style.css | 34 ++++++- src/Views/Order/NewOrder/NewOrderView.js | 30 ++++++ src/Views/Order/NewOrder/NewOrderView.php | 26 ++++++ .../Order/NewOrder/NewOrderView.style.css | 0 .../Order/NewOrder/NewOrderView.template.php | 26 ++++++ src/Views/Search/SearchView.js | 12 +++ src/Views/Search/SearchView.php | 2 +- src/Views/Search/SearchView.style.css | 91 +++++++++++++++++++ src/Views/Search/SearchView.template.php | 30 ++++++ 31 files changed, 473 insertions(+), 96 deletions(-) create mode 100644 src/Controllers/Order/NewOrderController.php create mode 100644 src/Controllers/Search/SearchController.php delete mode 100644 src/Models/Exceptions/BadRouteException.php create mode 100644 src/Models/Exceptions/CustomException.php delete mode 100644 src/Models/Exceptions/RouteException.php delete mode 100644 src/Models/Exceptions/RouteNotFoundException.php create mode 100644 src/Models/ViewModels/NewOrderViewModel.php create mode 100644 src/Views/Order/NewOrder/NewOrderView.js create mode 100644 src/Views/Order/NewOrder/NewOrderView.php create mode 100644 src/Views/Order/NewOrder/NewOrderView.style.css create mode 100644 src/Views/Order/NewOrder/NewOrderView.template.php diff --git a/public/index.php b/public/index.php index 47af34b..3b00851 100644 --- a/public/index.php +++ b/public/index.php @@ -23,10 +23,23 @@ # Intercept requests if necessary $interceptor = new Interceptor(); -$interceptor->handle(); +$interceptor->handleRequests(); # Call the router -$router = new Routing(); -$router->route(); +try +{ + $router = new Routing(); + $router->route(); + + if(http_response_code() === 404 || http_response_code() === 401 || http_response_code() === 403) + { + throw new Exception(); + } +} +catch (\Models\Exceptions\CustomException $e) +{ + $interceptor->handleErrorResponses($e); +} + diff --git a/src/Controllers/Account/AccountController.php b/src/Controllers/Account/AccountController.php index 31ff885..7f6d700 100644 --- a/src/Controllers/Account/AccountController.php +++ b/src/Controllers/Account/AccountController.php @@ -10,10 +10,16 @@ class AccountController { + function __construct() + { + JWTUtils::isAuthorized(); + } + public function get() { $data = new AccountViewModel(); $service = new AuthService(); + $userId = JWTUtils::getValue($_COOKIE["jwt"], "userId"); $data->User = $service->getUserById($userId); diff --git a/src/Controllers/BaseController.php b/src/Controllers/BaseController.php index 9a995c7..0cae8d6 100644 --- a/src/Controllers/BaseController.php +++ b/src/Controllers/BaseController.php @@ -2,26 +2,28 @@ namespace Controllers; +use Models\Exceptions\CustomException; + abstract class BaseController { public function get(): void { - http_response_code(405); + throw new CustomException(404, "La page n'existe pas"); } public function post(): void { - http_response_code(405); + throw new CustomException(405, "La méthode n'est pas autorisée"); } public function put(int $id): void { - http_response_code(405); + throw new CustomException(405, "La méthode n'est pas autorisée"); } public function patch(): void { - http_response_code(405); + throw new CustomException(405, "La méthode n'est pas autorisée"); } public function delete(int $id): void { - http_response_code(405); + throw new CustomException(405, "La méthode n'est pas autorisée"); } } \ No newline at end of file diff --git a/src/Controllers/Manage/ManageDVDController.php b/src/Controllers/Manage/ManageDVDController.php index 5fa2cff..265b4cc 100644 --- a/src/Controllers/Manage/ManageDVDController.php +++ b/src/Controllers/Manage/ManageDVDController.php @@ -5,6 +5,7 @@ use Controllers\BaseController; use Models\DVDModel; + use Models\Exceptions\CustomException; use Models\QueryModel\ManageDVDQueryModel; use Models\ViewModels\ManageDVDDetailViewModel; use Models\ViewModels\ManageDVDDetailViewStateEnum; @@ -108,7 +109,7 @@ public function put($id): void { if($id === null) { - http_response_code(502); + throw new CustomException(404, "La page n'existe pas pour cet identifiant"); } $model = new DVDModel(); diff --git a/src/Controllers/Order/NewOrderController.php b/src/Controllers/Order/NewOrderController.php new file mode 100644 index 0000000..c6d96b1 --- /dev/null +++ b/src/Controllers/Order/NewOrderController.php @@ -0,0 +1,48 @@ +Articles = []; + $data->TotalPrice = 0; + $data->User = $userService->getUserById($userId); + + if(isset($_COOKIE["cart"])) + { + $cart = json_decode($_COOKIE["cart"]); + foreach($cart->articles as $article) + { + $dvd = $service->getById($article->articleId); + $data->Articles[] = $dvd; + $data->TotalPrice += $dvd->price; + } + } + + $view = new NewOrderView($data); + $view->render(); + } + } +} \ No newline at end of file diff --git a/src/Controllers/Search/SearchController.php b/src/Controllers/Search/SearchController.php new file mode 100644 index 0000000..48989bf --- /dev/null +++ b/src/Controllers/Search/SearchController.php @@ -0,0 +1,38 @@ +setFromQueryString($_GET); + } + else + { + header('Location: ' . parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH)."?".$queryModel->getQueryString(), true, 303); + die(); + } + + $queryModel->Limit = 99; + $data->Query = $queryModel; + $data->DVDs = $service->getAll($queryModel, true); + + $view = new SearchView($data); + $view->render(); + } + } +} \ No newline at end of file diff --git a/src/Middlewares/Interceptor.php b/src/Middlewares/Interceptor.php index c786003..1db8e3f 100644 --- a/src/Middlewares/Interceptor.php +++ b/src/Middlewares/Interceptor.php @@ -2,11 +2,19 @@ namespace Middlewares { + + use Models\Exceptions\CustomException; + class Interceptor { - public function handle() + public function handleRequests() { } + + public function handleErrorResponses(CustomException $e) + { + echo "Une erreur est survenue: " . $e->getMessage() . "\n"; + } } } \ No newline at end of file diff --git a/src/Middlewares/Routing.php b/src/Middlewares/Routing.php index 9124427..99912a7 100644 --- a/src/Middlewares/Routing.php +++ b/src/Middlewares/Routing.php @@ -11,6 +11,9 @@ use Controllers\Home\HomeController; use Controllers\Manage\ManageDashboardController; use Controllers\Manage\ManageDVDController; + use Controllers\Order\NewOrderController; + use Controllers\Search\SearchController; + use Models\Exceptions\CustomException; class Routing { @@ -67,12 +70,18 @@ public function route(): void }, "/account" => function() { return new AccountController(); + }, + "/search" => function() { + return new SearchController(); + }, + "/neworder" => function() { + return new NewOrderController(); } ); - if(!is_callable($routeRegistry[$route])) + if(!key_exists($route, $routeRegistry) || !is_callable($routeRegistry[$route])) { - http_response_code(404); + throw new CustomException(404, "La page " . $route . " n'existe pas"); } else { diff --git a/src/Models/Exceptions/BadRouteException.php b/src/Models/Exceptions/BadRouteException.php deleted file mode 100644 index a0cb11d..0000000 --- a/src/Models/Exceptions/BadRouteException.php +++ /dev/null @@ -1,14 +0,0 @@ -user = $userConf; } else { - echo "C218_DataStore_User key not set"; - die(); + throw new CustomException(500, "Erreur interne: La configuration est manquante pour C218_DataStore_User"); } $passConf = getenv($this->DBConfBaseKey . "Pass"); @@ -34,8 +34,7 @@ protected function __construct() $this->pass = $passConf; } else { - echo "C218_DataStore_Pass key not set"; - die(); + throw new CustomException(500, "Erreur interne: La configuration est manquante pour C218_DataStore_Pass"); } $hostConf = getenv($this->DBConfBaseKey . "Host"); @@ -44,8 +43,7 @@ protected function __construct() $this->host = $hostConf; } else { - echo "C218_DataStore_Host key not set"; - die(); + throw new CustomException(500, "Erreur interne: La configuration est manquante pour C218_DataStore_Host"); } $dbNameConf = getenv($this->DBConfBaseKey . "DBName"); @@ -54,8 +52,7 @@ protected function __construct() $this->db = $dbNameConf; } else { - echo "C218_DataStore_DBName key not set"; - die(); + throw new CustomException(500, "Erreur interne: La configuration est manquante pour C218_DataStore_DBName"); } $this->dbContext = $this->getDBContext(); @@ -76,8 +73,7 @@ protected function getDBContext() return $this->dbContext; } catch (PDOException $e) { - echo "Connection failed: " . $e->getMessage(); - die(); + throw new CustomException(500, "Erreur interne: Echec de la connexion à la DB \r\n" . $e->getMessage()); } } @@ -141,8 +137,7 @@ private function validateStatementAndParameters($statement, $parameters): void if ($requiredParameters != $providedParameters) { - echo "Too few or too many parameters for the provided statement"; - die(); + throw new CustomException(500, "Erreur interne: Nombre de paramètres incorrect \r\n" . $e->getMessage()); } } } diff --git a/src/Utils/JWTUtils.php b/src/Utils/JWTUtils.php index 036ea13..ab077dd 100644 --- a/src/Utils/JWTUtils.php +++ b/src/Utils/JWTUtils.php @@ -4,6 +4,7 @@ use Exception; use InvalidArgumentException; + use Models\Exceptions\CustomException; /* https://www.freecodecamp.org/news/php-jwt-authentication-implementation/ Implementation standard de JWT @@ -123,8 +124,7 @@ public static function isAuthorized($admin = false) { if (empty($_COOKIE['jwt'])) { // Redirect to the login page if the user is not authenticated - http_response_code(401); - exit; + throw new CustomException(401, "L'utilisateur n'est pas authentifié"); } // Decode the JWT token to get user information @@ -135,8 +135,7 @@ public static function isAuthorized($admin = false) // If a specific role is required, check if the user has that role if (!$jwt["isAdmin"]) { // Redirect to an unauthorized page or show an error - http_response_code(403); - exit; + throw new CustomException(401, "L'utilisateur n'est pas autorisé"); } } return true; diff --git a/src/Views/BaseView.php b/src/Views/BaseView.php index d0ff2ae..8e5794b 100644 --- a/src/Views/BaseView.php +++ b/src/Views/BaseView.php @@ -4,6 +4,7 @@ { use Interfaces\IViewModel; + use Models\Exceptions\CustomException; use Models\ViewModels\LayoutViewModel; use Utils\JWTUtils; @@ -22,8 +23,7 @@ protected function renderLayout(LayoutViewModel $viewModel, IVIewModel|null $con { if(!isset($this->viewName)) { - error_log("No view name passed to the layout"); - die(); + throw new CustomException(500, "Erreur interne: Aucun nom passé au layout"); } $this->layoutData = $viewModel; diff --git a/src/Views/DVD/DVDView.style.css b/src/Views/DVD/DVDView.style.css index 3c11673..ba67ade 100644 --- a/src/Views/DVD/DVDView.style.css +++ b/src/Views/DVD/DVDView.style.css @@ -10,20 +10,24 @@ flex-direction: column; justify-content: space-around; background-color: #3B4252; - height: 100%; + min-height: 80%; border-radius: 5px; padding: 5px; + max-width: 800px; + overflow: auto; } .summary { display: flex; flex-direction: row; align-items: center; + margin: 0 auto; gap: 10px; } .details { display: flex; + flex-grow: 1; flex-direction: row; align-items: center; justify-content: space-around; @@ -40,11 +44,16 @@ color: white; } +.break { + flex-basis: 100%; + height: 0; +} + .button { position: absolute; right: 0; - top: 5px; - padding: 10px 20px; + top: 0; + padding: 10px; border: none; border-radius: 4px; background-color: #27994e; diff --git a/src/Views/DVD/DVDView.template.php b/src/Views/DVD/DVDView.template.php index abd4466..a9205fe 100644 --- a/src/Views/DVD/DVDView.template.php +++ b/src/Views/DVD/DVDView.template.php @@ -13,21 +13,25 @@ echo $data->dvd->LocalTitle; ?> -
- cart icon -
+
+
+ cart icon +
Jaquette dvd +
+ +
+

+ Synopsis +

dvd->Synopsis; ?>
-
- -
- +

Prix location diff --git a/src/Views/Home/HomeView.js b/src/Views/Home/HomeView.js index 519c23c..5028a3e 100644 --- a/src/Views/Home/HomeView.js +++ b/src/Views/Home/HomeView.js @@ -1,7 +1,12 @@ function search() { - let url = new URL(window.location.href); + let url = "/Search"; const query = document.querySelector('#dvd-search-input').value.trim(); + window.location.href = url + "?Search=" + query ; +} - url.searchParams.set("Search", query); - window.location.href = url.toString(); +function searchOnKeyDown(e) { + if(e.key === 'Enter') + { + search(); + } } \ No newline at end of file diff --git a/src/Views/Home/HomeView.template.php b/src/Views/Home/HomeView.template.php index 6e2e54b..6284260 100644 --- a/src/Views/Home/HomeView.template.php +++ b/src/Views/Home/HomeView.template.php @@ -6,7 +6,7 @@ ?> diff --git a/src/Views/Layout/Components/Cart.template.php b/src/Views/Layout/Components/Cart.template.php index 6a4d06f..8f8abc5 100644 --- a/src/Views/Layout/Components/Cart.template.php +++ b/src/Views/Layout/Components/Cart.template.php @@ -1,5 +1,6 @@
× +

Votre panier

- + cart icon diff --git a/src/Views/Layout/LayoutView.style.css b/src/Views/Layout/LayoutView.style.css index 2baf9d0..28c9871 100644 --- a/src/Views/Layout/LayoutView.style.css +++ b/src/Views/Layout/LayoutView.style.css @@ -71,14 +71,14 @@ main { } .cart-nav { - height: 100%; + height: calc(100% - 30px); width: 250px; position: fixed; z-index: 100; top: 0; right: -250px; background-color: #FF5E62; - padding-top: 60px; + padding-top: 30px; transition: left 0.5s ease; } @@ -164,17 +164,41 @@ a.sensitive { color: #111; } -.cart-item a { +.cart-item { + display: flex; +} + +.cart-item>a { + + flex-direction: row; font-size: 1em; text-align: center; } .cart-remove { - background-color: #353535; cursor:pointer; + text-align: center; + width: 46px; + height: 46px; + margin: auto; } .cart-remove:hover { - background-color: #575757; cursor:pointer; +} + +.cart-actions { + position: absolute; + width:100%; + bottom: 0; + display: flex; + flex-direction: column; + gap: 5px; +} + +.cart-actions > a { + border: 1px solid black; + width:100%; + text-align: center; + font-size: 1em; } \ No newline at end of file diff --git a/src/Views/Order/NewOrder/NewOrderView.js b/src/Views/Order/NewOrder/NewOrderView.js new file mode 100644 index 0000000..de8d398 --- /dev/null +++ b/src/Views/Order/NewOrder/NewOrderView.js @@ -0,0 +1,30 @@ +function addToCart(pId, pName) +{ + let cart = readCookie("cart"); + + if(cart && cart.articles) + { + let existingArticle = cart.articles.find(x => x.id === pId); + if(existingArticle === undefined) + { + cart.articles.push({ + id: pId, + name: pName, + quantity: 1 + }) + } + } + else + { + cart = { + articles: [{ + id: pId, + name: pName, + quantity: 1 + }] + } + } + + createCookie("cart", cart, 30); + location.reload(); +} \ No newline at end of file diff --git a/src/Views/Order/NewOrder/NewOrderView.php b/src/Views/Order/NewOrder/NewOrderView.php new file mode 100644 index 0000000..9e976f4 --- /dev/null +++ b/src/Views/Order/NewOrder/NewOrderView.php @@ -0,0 +1,26 @@ +viewName="Order/NewOrderView"; + $this->subTitle="NewOrder"; + $this->data = $viewModel; + } + + public function render(): void + { + $layoutData = new LayoutViewModel(); + $layoutData -> pageSubTitle = $this->subTitle; + parent::renderLayout($layoutData, $this->data, true); + } + } +} \ No newline at end of file diff --git a/src/Views/Order/NewOrder/NewOrderView.style.css b/src/Views/Order/NewOrder/NewOrderView.style.css new file mode 100644 index 0000000..e69de29 diff --git a/src/Views/Order/NewOrder/NewOrderView.template.php b/src/Views/Order/NewOrder/NewOrderView.template.php new file mode 100644 index 0000000..1635bd2 --- /dev/null +++ b/src/Views/Order/NewOrder/NewOrderView.template.php @@ -0,0 +1,26 @@ + + +

+ Votre panier +

+ +
+
+ +
+
+ +
+
+ +
+
+ diff --git a/src/Views/Search/SearchView.js b/src/Views/Search/SearchView.js index e69de29..5028a3e 100644 --- a/src/Views/Search/SearchView.js +++ b/src/Views/Search/SearchView.js @@ -0,0 +1,12 @@ +function search() { + let url = "/Search"; + const query = document.querySelector('#dvd-search-input').value.trim(); + window.location.href = url + "?Search=" + query ; +} + +function searchOnKeyDown(e) { + if(e.key === 'Enter') + { + search(); + } +} \ No newline at end of file diff --git a/src/Views/Search/SearchView.php b/src/Views/Search/SearchView.php index d598ad9..7e27675 100644 --- a/src/Views/Search/SearchView.php +++ b/src/Views/Search/SearchView.php @@ -21,7 +21,7 @@ public function render(): void { $layoutData = new LayoutViewModel(); $layoutData -> pageSubTitle = $this->subTitle; - parent::renderLayout($layoutData, $this->data); + parent::renderLayout($layoutData, $this->data, true); } } } \ No newline at end of file diff --git a/src/Views/Search/SearchView.style.css b/src/Views/Search/SearchView.style.css index e69de29..f418b31 100644 --- a/src/Views/Search/SearchView.style.css +++ b/src/Views/Search/SearchView.style.css @@ -0,0 +1,91 @@ +.search-bar { + display: flex; + justify-content: center; + margin-bottom: 20px; +} + +.search-input { + padding: 10px; + border: none; + border-radius: 4px 0 0 4px; + width: 70%; + background-color: #3B4252; + color: #D8DEE9; + font-size: 16px; +} + +.search-button { + padding: 10px 20px; + border: none; + border-radius: 0 4px 4px 0; + background-color: #81A1C1; + color: white; + cursor: pointer; +} + +.search-button:hover { + background-color: #88C0D0; +} + +.card-container { + display: flex; + flex-direction: row; + flex-wrap: wrap; + gap: 20px; + justify-content: flex-start; + overflow: auto; +} + +.card { + background-color: #2E3440; + flex-basis: 15%; + border-radius: 8px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + box-shadow: 5px 5px 2px rgba(0, 0, 0, 0.2); + position: relative; + overflow: hidden; + min-width: 128px; + max-width: 256px; + scale: 0.9; +} + +.card:hover { + scale: 1; + z-index: 99; +} + +.card-content { + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: center; + width: 100%; + height: 100%; + margin: auto; +} + + +.card-content img { + max-width: 100%; + max-height: 100%; + display: block; + margin: auto; +} + +.home-category { + padding: 10px; +} + +.home-category:nth-child(odd) { + background: linear-gradient(135deg, #4A4E69, #2B2D42); +} + +.home-category h3 { + text-align: left; + margin-left: 20px; + color: white; + text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5); +} \ No newline at end of file diff --git a/src/Views/Search/SearchView.template.php b/src/Views/Search/SearchView.template.php index e69de29..61febfb 100644 --- a/src/Views/Search/SearchView.template.php +++ b/src/Views/Search/SearchView.template.php @@ -0,0 +1,30 @@ + + + + +
+ DVDs) == 0) { echo "

Pas de résultats

"; } ?> + DVDs as $row): ?> +
+
+ Image): ?> + preview + +
+ No Preview Available +
+ + + +
+
+ +