From b17396a2d96ab01c1d93dc7f0fbfa9059d620914 Mon Sep 17 00:00:00 2001 From: csauder <christophe.sauder@viaevista.fr> Date: Fri, 12 Jan 2024 15:18:14 +0100 Subject: [PATCH] :sparkles: Edition de bureau via un formulaire moche --- composer.json | 3 +- config/bootstrap.php | 2 - config/container.php | 6 ++- config/routes.php | 7 +++ public/assets/script.js | 43 ++++++++++++++++ public/assets/style.css | 62 +++++++++++++++++++++++ src/Console/CreateDatabaseCommand.php | 6 +++ src/Console/PopulateDatabaseCommand.php | 28 +++++------ src/Controller/OfficeController.php | 66 +++++++++++++++++++++++++ src/Models/Company.php | 3 ++ view/company/index.twig | 3 ++ view/layout.twig | 13 +++++ view/office/form.twig | 37 ++++++++++++++ 13 files changed, 261 insertions(+), 18 deletions(-) create mode 100644 src/Controller/OfficeController.php create mode 100644 view/office/form.twig diff --git a/composer.json b/composer.json index 65c5c82..c5203bc 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,8 @@ "slim/twig-view": "^3.3", "slim/flash": "^0.4.0", "illuminate/database": "^10.40", - "symfony/console": "^7.0" + "symfony/console": "^7.0", + "ext-http": "*" }, "autoload": { "psr-4": { diff --git a/config/bootstrap.php b/config/bootstrap.php index 29fd374..3302c82 100644 --- a/config/bootstrap.php +++ b/config/bootstrap.php @@ -12,8 +12,6 @@ $containerBuilder->addDefinitions(__DIR__ . '/container.php'); $container = $containerBuilder->build(); $app = $container->get(App::class); -$app->addRoutingMiddleware(); -$app->add(TwigMiddleware::createFromContainer($app)); (require __DIR__ . '/routes.php')($app); diff --git a/config/container.php b/config/container.php index 4ffe6ec..cb0173f 100644 --- a/config/container.php +++ b/config/container.php @@ -5,6 +5,7 @@ use Slim\Factory\AppFactory; use Slim\Middleware\ErrorMiddleware; use Psr\Container\ContainerInterface; use Slim\Views\Twig; +use Slim\Views\TwigMiddleware; return [ 'settings' => function () { @@ -22,7 +23,10 @@ return [ App::class => function (ContainerInterface $container) { $container->get('db'); AppFactory::setContainer($container); - return AppFactory::create(); + $app = AppFactory::create(); + $app->addRoutingMiddleware(); + $app->add(TwigMiddleware::createFromContainer($app)); + return $app; }, ErrorMiddleware::class => function (ContainerInterface $container) { $app = $container->get(App::class); diff --git a/config/routes.php b/config/routes.php index 0fa6d09..c36ed6f 100644 --- a/config/routes.php +++ b/config/routes.php @@ -3,9 +3,16 @@ use Slim\App; use App\Controller\CompanyController; use App\Controller\HomeController; +use App\Controller\OfficeController; return function (App $app) { // On gère la route "par défaut" de l'application $app->get('/', HomeController::class . ':index'); + + // CompanyController $app->get('/company/{id}', CompanyController::class . ':index'); + + // OfficeController + $app->get('/office/{id}/edit', OfficeController::class . ':editGet'); + $app->post('/office/{id}/edit', OfficeController::class . ':editPost'); }; diff --git a/public/assets/script.js b/public/assets/script.js index e69de29..a7ee801 100644 --- a/public/assets/script.js +++ b/public/assets/script.js @@ -0,0 +1,43 @@ +const modal = document.querySelector('#modal-wrapper'); +const modalContent = document.querySelector('#modal-content'); +const modalTitle = document.querySelector('#modal-title'); + +function openModal() { +modal.classList.add('active'); +} + +function closeModal() { + modal.classList.remove('active') + modalContent.innerHTML = "" +} + +function loadModalContent(url, title) { +fetch(url) +.then(response => response.text()) +.then(data => { +modalContent.innerHTML = data; +modalTitle.innerHTML = title; +openModal() +}) +.catch(error => console.log(error)) +} + +// On gère la fermeture du modal +function init() { + +document.querySelector('#close-modal').addEventListener("click", closeModal) +document.addEventListener('keydown', event => { + if (event.key == 'Escape') closeModal(); + +}) + +document.querySelector('.offices-list').addEventListener('click', event => { + if (event.target.classList.contains('office-edit')) { + loadModalContent(`/office/${event.target.dataset.officeId}/edit`, 'Édition du bureau') + } +}) + +} + + +document.addEventListener('DOMContentLoaded', init) diff --git a/public/assets/style.css b/public/assets/style.css index dccb62a..83b83ca 100644 --- a/public/assets/style.css +++ b/public/assets/style.css @@ -79,3 +79,65 @@ html, body { width: 60px; color: var(--accent-color); } + +.button, button, input[type="submit"] { + background: var(--main-color); + color: var(--main-bg-color); + border-radius: 2px; + border: none; + padding: 0.5em 1em; + cursor: pointer; + transition: all 0.2s ease-in-out; +} + +.button:hover, button:hover, input[type="submit"]:hover { + background: var(--accent-color); +} + +.button:active, button:active, input[type="submit"]:active { + transform: scale(0.95); +} + +.text-center { + text-align: center; +} + +.modal-wrapper { + position: fixed; + top: 0; + left: 0; + z-index: 100; + width: 100%; + height: 100%; + background-color: rgba(0,0,0,0.5); + display: none; + justify-content: center; + align-items: center; +} + +#modal { + background-color: var(--main-bg-color); + padding: 1em ; + border-radius: 5px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); + width: 80%; + max-width: 600px; +} + +.modal-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1em; +} + +.modal-content { + margin-bottom: 1em; +} + +.active { + display: flex; +} + + + diff --git a/src/Console/CreateDatabaseCommand.php b/src/Console/CreateDatabaseCommand.php index bc0fa5c..1899520 100644 --- a/src/Console/CreateDatabaseCommand.php +++ b/src/Console/CreateDatabaseCommand.php @@ -42,6 +42,8 @@ class CreateDatabaseCommand extends Command email VARCHAR(255) , website VARCHAR(255) , image VARCHAR(255) , + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, head_office_id INT , PRIMARY KEY (id) ); @@ -59,6 +61,8 @@ class CreateDatabaseCommand extends Command email VARCHAR(255) , phone VARCHAR(255) , company_id INT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), FOREIGN KEY (company_id) REFERENCES companies (id) ); @@ -74,6 +78,8 @@ class CreateDatabaseCommand extends Command email VARCHAR(255) , phone VARCHAR(255) , job_title VARCHAR(255) , + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), FOREIGN KEY (office_id) REFERENCES offices (id) ); diff --git a/src/Console/PopulateDatabaseCommand.php b/src/Console/PopulateDatabaseCommand.php index 8c8fbde..baaaeef 100644 --- a/src/Console/PopulateDatabaseCommand.php +++ b/src/Console/PopulateDatabaseCommand.php @@ -42,26 +42,26 @@ class PopulateDatabaseCommand extends Command $db->getConnection()->statement("INSERT INTO `companies` VALUES - (1,'Stack Exchange','0601010101','stack@exchange.com','https://stackexchange.com/','https://i.stack.imgur.com/UPdHB.jpg',null), - (2,'Google','0602020202','contact@google.com','https://www.google.com','https://upload.wikimedia.org/wikipedia/commons/thumb/e/e0/Google_office_%284135991953%29.jpg/800px-Google_office_%284135991953%29.jpg?20190722090506',null) + (1,'Stack Exchange','0601010101','stack@exchange.com','https://stackexchange.com/','https://i.stack.imgur.com/UPdHB.jpg', now(), now(), null), + (2,'Google','0602020202','contact@google.com','https://www.google.com','https://upload.wikimedia.org/wikipedia/commons/thumb/e/e0/Google_office_%284135991953%29.jpg/800px-Google_office_%284135991953%29.jpg?20190722090506',now(), now(), null) "); $db->getConnection()->statement("INSERT INTO `offices` VALUES - (1,'Bureau de Nancy','1 rue Stanistlas','Nancy','54000','France','nancy@stackexchange.com',NULL,1), - (2,'Burea de Vandoeuvre','46 avenue Jeanne d\'Arc','Vandoeuvre','54500','France',NULL,NULL,1), - (3,'Siege sociale','2 rue de la primatiale','Paris','75000','France',NULL,NULL,2), - (4,'Bureau Berlinois','192 avenue central','Berlin','12277','Allemagne',NULL,NULL,2) + (1,'Bureau de Nancy','1 rue Stanistlas','Nancy','54000','France','nancy@stackexchange.com',NULL,1, now(), now()), + (2,'Burea de Vandoeuvre','46 avenue Jeanne d\'Arc','Vandoeuvre','54500','France',NULL,NULL,1, now(), now()), + (3,'Siege sociale','2 rue de la primatiale','Paris','75000','France',NULL,NULL,2, now(), now()), + (4,'Bureau Berlinois','192 avenue central','Berlin','12277','Allemagne',NULL,NULL,2, now(), now()) "); $db->getConnection()->statement("INSERT INTO `employees` VALUES - (1,'Camille','La Chenille',1,'camille.la@chenille.com',NULL,'Ingénieur'), - (2,'Albert','Mudhat',2,'albert.mudhat@aqume.net',NULL,'Superviseur'), - (3,'Sylvie','Tesse',3,'sylive.tesse@factice.local',NULL,'PDG'), - (4,'John','Doe',4,'john.doe@generique.org',NULL,'Testeur'), - (5,'Jean','Bon',1,'jean@test.com',NULL,'Developpeur'), - (6,'Anais','Dufour',2,'anais@aqume.net',NULL,'DBA'), - (7,'Sylvain','Poirson',3,'sylvain@factice.local',NULL,'Administrateur réseau'), - (8,'Telma','Thiriet',4,'telma@generique.org',NULL,'Juriste') + (1,'Camille','La Chenille',1,'camille.la@chenille.com',NULL,'Ingénieur', now(), now()), + (2,'Albert','Mudhat',2,'albert.mudhat@aqume.net',NULL,'Superviseur', now(), now()), + (3,'Sylvie','Tesse',3,'sylive.tesse@factice.local',NULL,'PDG', now(), now()), + (4,'John','Doe',4,'john.doe@generique.org',NULL,'Testeur', now(), now()), + (5,'Jean','Bon',1,'jean@test.com',NULL,'Developpeur', now(), now()), + (6,'Anais','Dufour',2,'anais@aqume.net',NULL,'DBA', now(), now()), + (7,'Sylvain','Poirson',3,'sylvain@factice.local',NULL,'Administrateur réseau', now(), now()), + (8,'Telma','Thiriet',4,'telma@generique.org',NULL,'Juriste', now(), now()) "); $db->getConnection()->statement("update companies set head_office_id = 1 where id = 1;"); diff --git a/src/Controller/OfficeController.php b/src/Controller/OfficeController.php new file mode 100644 index 0000000..1cde511 --- /dev/null +++ b/src/Controller/OfficeController.php @@ -0,0 +1,66 @@ +<?php + +namespace App\Controller; + +use App\Models\Company; +use App\Models\Office; +use http\Exception\InvalidArgumentException; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Slim\Exception\HttpNotFoundException; + + +class OfficeController extends DefaultController +{ + public function editGet(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface + { + $office = $this->findOfficeById($request); + + return $this->twig->render($response, 'office/form.twig', ['office' => $office]); + } + + public function editPost(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface + { + $office = $this->findOfficeById($request); + $officeQuery = $request->getParsedBody(); + if ($officeQuery['officeName'] !== null && $officeQuery['officeName'] !== $office->name) { + $office->name = $officeQuery['officeName']; + } + if ($officeQuery['officeAddress'] !== null && $officeQuery['officeAddress'] !== $office->address) { + $office->address = $officeQuery['officeAddress']; + } + if ($officeQuery['officeCity'] !== null && $officeQuery['officeCity'] !== $office->city) { + $office->city = $officeQuery['officeCity']; + } + if ($officeQuery['officeZipCode'] !== null && $officeQuery['officeZipCode'] !== $office->zip_code) { + $office->zip_code = $officeQuery['officeZipCode']; + } + if ($officeQuery['officeCountry'] !== null && $officeQuery['officeCountry'] !== $office->country) { + $office->country = $officeQuery['officeCountry']; + } + if ($officeQuery['officeEmail'] !== null && $officeQuery['officeEmail'] !== $office->email) { + $office->email = $officeQuery['officeEmail']; + } + if ($officeQuery['officePhone'] !== null && $officeQuery['officePhone'] !== $office->phone) { + $office->phone = $officeQuery['officePhone']; + } + $office->save(); + return $response->withStatus(302)->withHeader('Location', '/company/' . $office->company->id); + } + + + private function findOfficeById(ServerRequestInterface $request): Office + { + $officeId = $request->getAttribute('id'); + // On vérifie que l'id est bien un int + if (!is_numeric($officeId)) { + throw new InvalidArgumentException('Invalid office id format'); + } + + $office = Office::find($request->getAttribute('id')); + if (!$office) { + throw new HttpNotFoundException($request); + } + return $office; + } +} diff --git a/src/Models/Company.php b/src/Models/Company.php index 1327b4f..2763e4c 100644 --- a/src/Models/Company.php +++ b/src/Models/Company.php @@ -7,6 +7,9 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasOne; +/** + * @method static find($id) + */ class Company extends Model { protected $table = 'companies'; diff --git a/view/company/index.twig b/view/company/index.twig index 273d4ca..96ecc12 100644 --- a/view/company/index.twig +++ b/view/company/index.twig @@ -10,6 +10,9 @@ {% for office in company.offices %} <div class="office"> <h2>{{ office.name }}</h2> + <div class="text-center"> + <button class="office-edit" data-office-id="{{ office.id }}">✏️ Éditer</button> + </div> <p>Siège social: {{ office.headOffice ? 'Oui' : 'Non' }}</p> <p>Adresse: {{ office.getFullAddressAttribute() }}</p> <div> diff --git a/view/layout.twig b/view/layout.twig index 3235bf2..e46caf8 100644 --- a/view/layout.twig +++ b/view/layout.twig @@ -12,6 +12,19 @@ <body> {% block body %}{% endblock %} + +<div id="modal-wrapper" class="modal-wrapper"> + <div id="modal"> + <div class="modal-header"> + <h2 id="modal-title">Titre</h2> + <button id="close-modal">X</button> + </div> + <div id="modal-content"> + Bonjour + </div> + </div> +</div> + {% block scripts %} <script src="/assets/script.js"></script> {% endblock %} diff --git a/view/office/form.twig b/view/office/form.twig new file mode 100644 index 0000000..54f60bd --- /dev/null +++ b/view/office/form.twig @@ -0,0 +1,37 @@ +<form id="officeForm" method="post" action="/office/{{ office.id }}/edit"> + <div> + <label for="officeID">ID du bureau</label> + <input type="text" name="office ID" id="officeID" value="{{ office.id }}" disabled/> + </div> + <div> + <label for="officeName">Nom du bureau</label> + <input type="text" maxlength="255" name="officeName" id="officeName" value="{{ office.name }}" /> + </div> + <div> + <label for="officeAddress">Adresse</label> + <input type="text" maxlength="255" name="officeAddress" id="officeAddress" value="{{ office.address }}" /> + </div> + <div> + <label for="officeCity">Ville</label> + <input type="text" maxlength="255" name="officeCity" id="officeCity" value="{{ office.city }}" /> + </div> + <div> + <label for="officeZipCode">Code Postal</label> + <input type="text" maxlength="255" name="officeZipCode" id="officeZipCode" value="{{ office.zip_code }}" /> + </div> + <div> + <label for="officeCountry">Pays</label> + <input type="text" maxlength="255" name="officeCountry" id="officeCountry" value="{{ office.country }}" /> + </div> + <div> + <label for="officeEmail">Email</label> + <input type="email" maxlength="255" name="officeEmail" id="officeEmail" value="{{ office.email }}" /> + </div> + <div> + <label for="officePhone">Telephone</label> + <input type="tel" maxlength="255" name="officePhone" id="officePhone" value="{{ office.phone }}" /> + </div> + <div> + <input type="submit" value="Enregistrer" /> + </div> +</form> -- GitLab