From 858a5bed06845f689b8c357062b7dbadeabd380a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Guillot?= Date: Sat, 29 Jun 2024 14:51:10 +0200 Subject: [PATCH] Added: - toolbar and fixes --- Makefile | 6 ++ assets/controllers/dropdown_controller.js | 45 ++++++++++++++ assets/controllers/toolbar_controller.js | 59 +++++++++++++++++++ package-lock.json | 21 +++++++ package.json | 1 + src/Controller/ActivityController.php | 8 ++- src/Controller/MangaController.php | 16 +++-- src/Manager/ToolbarManager.php | 41 +++++++++++++ src/Repository/MangaRepository.php | 27 +++++++-- src/Twig/Components/DropdownMenu.php | 16 +++++ src/Twig/Components/NewMangaForm.php | 12 +++- templates/activity/index.html.twig | 50 +++++++++++----- templates/components/Divider.html.twig | 2 + templates/components/DropdownMenu.html.twig | 29 +++++++++ templates/components/MangaSearch.html.twig | 2 +- templates/components/Search.html.twig | 4 +- templates/components/ToolBarButton.html.twig | 10 +++- templates/components/Toolbar.html.twig | 11 ++++ templates/manga/index.html.twig | 62 +++++++++++++------- templates/manga/show_chapters.html.twig | 50 +++++++++++----- 20 files changed, 404 insertions(+), 68 deletions(-) create mode 100644 assets/controllers/dropdown_controller.js create mode 100644 assets/controllers/toolbar_controller.js create mode 100644 src/Manager/ToolbarManager.php create mode 100644 src/Twig/Components/DropdownMenu.php create mode 100644 templates/components/Divider.html.twig create mode 100644 templates/components/DropdownMenu.html.twig create mode 100644 templates/components/Toolbar.html.twig diff --git a/Makefile b/Makefile index 9cb71a6..55fe6a2 100644 --- a/Makefile +++ b/Makefile @@ -150,3 +150,9 @@ npm-run: ## Run the dev server npm-watch: ## Watch for changes @$(DOCKER_COMP) exec node npm run watch + +npm-add: ## Add a package as a dependency make npm-add p=package-name + @$(DOCKER_COMP) exec node npm install $(p) + +npm-add-dev: ## Add a package as a dev dependency make npm-add-dev p=package-name + @$(DOCKER_COMP) exec node npm install $(p) --save-dev diff --git a/assets/controllers/dropdown_controller.js b/assets/controllers/dropdown_controller.js new file mode 100644 index 0000000..25989ea --- /dev/null +++ b/assets/controllers/dropdown_controller.js @@ -0,0 +1,45 @@ +// assets/controllers/dropdown_controller.js +import {Controller} from "@hotwired/stimulus" +import {useClickOutside} from "stimulus-use" + +export default class extends Controller { + static targets = ["button", "menu"] + + connect() { + useClickOutside(this) + } + + toggle(event) { + this.menuTarget.classList.toggle('hidden') + if (!this.menuTarget.classList.contains('hidden')) { + this.positionMenu() + } + } + + clickOutside(event) { + this.menuTarget.classList.add('hidden') + } + + positionMenu() { + const buttonRect = this.buttonTarget.getBoundingClientRect() + const menuRect = this.menuTarget.getBoundingClientRect() + const spaceRight = window.innerWidth - buttonRect.right + const spaceBottom = window.innerHeight - buttonRect.bottom + + if (spaceRight < menuRect.width && buttonRect.left > menuRect.width) { + this.menuTarget.style.left = 'auto' + this.menuTarget.style.right = '0' + } else { + this.menuTarget.style.left = '0' + this.menuTarget.style.right = 'auto' + } + + if (spaceBottom < menuRect.height && buttonRect.top > menuRect.height) { + this.menuTarget.style.top = 'auto' + this.menuTarget.style.bottom = '100%' + } else { + this.menuTarget.style.top = '100%' + this.menuTarget.style.bottom = 'auto' + } + } +} diff --git a/assets/controllers/toolbar_controller.js b/assets/controllers/toolbar_controller.js new file mode 100644 index 0000000..e4eeadd --- /dev/null +++ b/assets/controllers/toolbar_controller.js @@ -0,0 +1,59 @@ +// assets/controllers/toolbar_controller.js +import { Controller } from "@hotwired/stimulus" + +export default class extends Controller { + static targets = ["dropdown"] + static values = { + currentSort: String, + currentOrder: String + } + + refresh() { + console.log("Refreshing...") + } + + syncRss() { + console.log("Syncing RSS...") + } + + search() { + console.log("Searching...") + } + + import() { + console.log("Importing...") + } + + editMangas() { + console.log("Editing mangas...") + } + + showOptions() { + console.log("Showing options...") + } + + changeView() { + console.log("Changing view...") + } + + sort(event) { + event.preventDefault() + const sortOption = event.currentTarget.dataset.sortOption + let order = 'asc' + + if (sortOption === this.currentSortValue && this.currentOrderValue === 'asc') { + order = 'desc' + } + + const url = new URL(window.location) + url.searchParams.set('sort', sortOption) + url.searchParams.set('order', order) + + window.location = url.toString() + } + + filter() { + console.log("Filtering...") + } + +} diff --git a/package-lock.json b/package-lock.json index dea7531..bcf8c50 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,6 +26,7 @@ "regenerator-runtime": "^0.13.9", "sass": "^1.59.3", "sass-loader": "^13.2.0", + "stimulus-use": "^0.52.2", "webpack": "^5.74.0", "webpack-cli": "^4.10.0", "webpack-notifier": "^1.15.0" @@ -5706,6 +5707,16 @@ "node": ">= 0.4" } }, + "node_modules/hotkeys-js": { + "version": "3.13.7", + "resolved": "https://registry.npmjs.org/hotkeys-js/-/hotkeys-js-3.13.7.tgz", + "integrity": "sha512-ygFIdTqqwG4fFP7kkiYlvayZppeIQX2aPpirsngkv1xM1lP0piDY5QEh68nQnIKvz64hfocxhBaD/uK3sSK1yQ==", + "dev": true, + "peer": true, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + } + }, "node_modules/hpack.js": { "version": "2.1.6", "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", @@ -9207,6 +9218,16 @@ "node": ">= 0.8" } }, + "node_modules/stimulus-use": { + "version": "0.52.2", + "resolved": "https://registry.npmjs.org/stimulus-use/-/stimulus-use-0.52.2.tgz", + "integrity": "sha512-413+tIw9n6Jnb0OFiQE1i3aP01i0hhGgAnPp1P6cNuBbhhqG2IOp8t1O/4s5Tw2lTvSYrFeLNdaY8sYlDaULeg==", + "dev": true, + "peerDependencies": { + "@hotwired/stimulus": ">= 3", + "hotkeys-js": ">= 3" + } + }, "node_modules/streamx": { "version": "2.18.0", "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.18.0.tgz", diff --git a/package.json b/package.json index 7250549..553c6e8 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "regenerator-runtime": "^0.13.9", "sass": "^1.59.3", "sass-loader": "^13.2.0", + "stimulus-use": "^0.52.2", "webpack": "^5.74.0", "webpack-cli": "^4.10.0", "webpack-notifier": "^1.15.0" diff --git a/src/Controller/ActivityController.php b/src/Controller/ActivityController.php index 05090b4..42f76d7 100644 --- a/src/Controller/ActivityController.php +++ b/src/Controller/ActivityController.php @@ -2,6 +2,7 @@ namespace App\Controller; +use App\Manager\ToolbarManager; use App\Message\DownloadChapter; use App\Repository\ChapterRepository; use Doctrine\DBAL\Connection; @@ -13,7 +14,11 @@ use Symfony\Component\Routing\Annotation\Route; class ActivityController extends AbstractController { - public function __construct(private Connection $connection, private readonly ChapterRepository $chapterRepository) + public function __construct( + private readonly Connection $connection, + private readonly ChapterRepository $chapterRepository, + private readonly ToolbarManager $toolbarManager + ) { } @@ -33,6 +38,7 @@ class ActivityController extends AbstractController return $this->render('activity/index.html.twig', [ 'controller_name' => 'ActivityController', 'status' => $status, + 'toolbarItems' => $this->toolbarManager->getToolbarItems(), ]); } diff --git a/src/Controller/MangaController.php b/src/Controller/MangaController.php index 29cac02..0629ffc 100644 --- a/src/Controller/MangaController.php +++ b/src/Controller/MangaController.php @@ -4,6 +4,7 @@ namespace App\Controller; use App\Entity\Chapter; use App\Entity\Manga; +use App\Manager\ToolbarManager; use App\Message\DownloadChapter; use App\Repository\ChapterRepository; use App\Repository\MangaRepository; @@ -34,19 +35,23 @@ class MangaController extends AbstractController private readonly ChapterRepository $chapterRepository, private readonly MangaUpdatesMetadataProvider $mangaUpdatesDbProvider, private readonly MessageBusInterface $bus, - private readonly CbzService $cbzService + private readonly CbzService $cbzService, + private readonly ToolbarManager $toolbarManager ) { } #[Route('/manga', name: 'app_manga')] - public function index(): Response + public function index(Request $request): Response { -// phpinfo(); - $mangas = $this->mangaRepository->findAll(); + $sort = $request->query->get('sort', 'title'); + $order = $request->query->get('order', 'asc'); + + $mangas = $this->mangaRepository->findAllSorted($sort, $order); + return $this->render('manga/index.html.twig', [ - 'controller_name' => 'MangaController', 'mangas' => $mangas, + 'toolbarItems' => $this->toolbarManager->getToolbarItems(), ]); } @@ -89,6 +94,7 @@ class MangaController extends AbstractController return $this->render('manga/show_chapters.html.twig', [ 'chapters_by_volume' => $chaptersByVolume, 'manga' => $manga, + 'toolbarItems' => $this->toolbarManager->getToolbarItems(), ]); } diff --git a/src/Manager/ToolbarManager.php b/src/Manager/ToolbarManager.php new file mode 100644 index 0000000..0693638 --- /dev/null +++ b/src/Manager/ToolbarManager.php @@ -0,0 +1,41 @@ + $this->getSortItems(), + 'filterItems' => $this->getFilterItems(), + 'viewOptions' => $this->getViewOptions() + ]; + } + + private function getSortItems(): array + { + return [ + ['text' => 'Par titre', 'action' => 'sort', 'data' => ['sort-option' => 'title']], + ['text' => 'Par année de publication', 'action' => 'sort', 'data' => ['sort-option' => 'publicationYear']], + ['text' => 'Par date d\'ajout', 'action' => 'sort', 'data' => ['sort-option' => 'createdAt']] + ]; + } + + private function getFilterItems(): array + { + return [ + ['text' => 'Tous les genres', 'action' => 'filter', 'data' => ['filter-option' => 'allGenres']], + ['text' => 'Mangas terminés', 'action' => 'filter', 'data' => ['filter-option' => 'completed']], + ['text' => 'Mangas en cours', 'action' => 'filter', 'data' => ['filter-option' => 'ongoing']] + ]; + } + + private function getViewOptions(): array + { + return [ + ['text' => 'Vue grille', 'action' => 'changeView', 'data' => ['view-option' => 'grid']], + ['text' => 'Vue liste', 'action' => 'changeView', 'data' => ['view-option' => 'list']] + ]; + } +} diff --git a/src/Repository/MangaRepository.php b/src/Repository/MangaRepository.php index 2499f77..29fce6b 100644 --- a/src/Repository/MangaRepository.php +++ b/src/Repository/MangaRepository.php @@ -87,11 +87,6 @@ class MangaRepository extends ServiceEntityRepository return $sortedEntities; } - private function normalizeSlug(string $slug): string - { - return strtolower(preg_replace('/[^a-z0-9]+/i', '', $slug)); - } - /** * @throws NonUniqueResultException */ @@ -108,6 +103,28 @@ class MangaRepository extends ServiceEntityRepository return $query->getQuery()->getOneOrNullResult(); } + public function findAllSorted(string $sort = 'title', string $order = 'asc'): array + { + $qb = $this->createQueryBuilder('m'); + + switch ($sort) { + case 'title': + $qb->orderBy('m.title', $order); + break; + case 'publicationYear': + $qb->orderBy('m.publicationYear', $order); + break; + case 'createdAt': + $qb->orderBy('m.createdAt', $order); + break; + // Ajoutez d'autres cas pour les différentes options de tri + default: + $qb->orderBy('m.title', 'asc'); + } + + return $qb->getQuery()->getResult(); + } + // /** // * @return Manga[] Returns an array of Manga objects // */ diff --git a/src/Twig/Components/DropdownMenu.php b/src/Twig/Components/DropdownMenu.php new file mode 100644 index 0000000..eee97e3 --- /dev/null +++ b/src/Twig/Components/DropdownMenu.php @@ -0,0 +1,16 @@ +manga = $manga; @@ -84,6 +90,8 @@ class NewMangaForm $manga->addChapter($chapter); } + $mangaChapterUrl = $this->urlGenerator->generate('app_manga_show', ['mangaSlug' => $manga->getSlug()]); + try { foreach ($manga->getChapters() as $chapter) { $entityManager->persist($chapter); @@ -93,11 +101,11 @@ class NewMangaForm $entityManager->flush(); } catch (\Exception $e) { if ($e instanceof UniqueConstraintViolationException) { - return new RedirectResponse('/manga/' . $manga->getSlug()); + return new RedirectResponse($mangaChapterUrl); } throw $e; } - return new RedirectResponse('/manga/' . $manga->getSlug()); + return new RedirectResponse($mangaChapterUrl); } } diff --git a/templates/activity/index.html.twig b/templates/activity/index.html.twig index a7f9c23..af2dd64 100644 --- a/templates/activity/index.html.twig +++ b/templates/activity/index.html.twig @@ -1,19 +1,41 @@ {% extends 'base.html.twig' %} {% block toolbar %} -
-
-
- - -
- - -
- - -
-
-
+ {% set left_group %} + + + + + + + + + {% endset %} + + {% set right_group %} + + + + + + {% endset %} + + {% endblock %} {% block body %}
diff --git a/templates/components/Divider.html.twig b/templates/components/Divider.html.twig new file mode 100644 index 0000000..6ce2b59 --- /dev/null +++ b/templates/components/Divider.html.twig @@ -0,0 +1,2 @@ +{# templates/components/Divider.html.twig #} +
diff --git a/templates/components/DropdownMenu.html.twig b/templates/components/DropdownMenu.html.twig new file mode 100644 index 0000000..bd9f09c --- /dev/null +++ b/templates/components/DropdownMenu.html.twig @@ -0,0 +1,29 @@ +{# templates/components/DropdownMenu.html.twig #} +
+
+ + +
+
diff --git a/templates/components/MangaSearch.html.twig b/templates/components/MangaSearch.html.twig index 7b8aeaa..0491f22 100644 --- a/templates/components/MangaSearch.html.twig +++ b/templates/components/MangaSearch.html.twig @@ -35,7 +35,7 @@ {{ manga.title }} ({{ manga.publicationYear }})
- diff --git a/templates/components/Search.html.twig b/templates/components/Search.html.twig index 70c1cc5..fd6b583 100644 --- a/templates/components/Search.html.twig +++ b/templates/components/Search.html.twig @@ -16,7 +16,7 @@
  • Mangas existants
  • {% for manga in this.mangas %}
  • - + {{ manga.title }} {{ manga.title }} ({{ manga.publicationYear }}) @@ -26,7 +26,7 @@ {% else %}
  • Aucun manga trouvé.
  • - + Ajouter {{ query }}
  • diff --git a/templates/components/ToolBarButton.html.twig b/templates/components/ToolBarButton.html.twig index 29f9f1f..9c09c26 100644 --- a/templates/components/ToolBarButton.html.twig +++ b/templates/components/ToolBarButton.html.twig @@ -1,5 +1,11 @@ - - diff --git a/templates/components/Toolbar.html.twig b/templates/components/Toolbar.html.twig new file mode 100644 index 0000000..05644c8 --- /dev/null +++ b/templates/components/Toolbar.html.twig @@ -0,0 +1,11 @@ +{# templates/components/Toolbar.html.twig #} +
    +
    +
    + {{ left_group|raw }} +
    +
    + {{ right_group|raw }} +
    +
    +
    diff --git a/templates/manga/index.html.twig b/templates/manga/index.html.twig index d6bc946..1fa75a2 100644 --- a/templates/manga/index.html.twig +++ b/templates/manga/index.html.twig @@ -1,31 +1,49 @@ {% extends 'base.html.twig' %} {% block toolbar %} -
    -
    -
    - - -
    - - -
    - -
    -
    - -
    - - - -
    -
    -
    + {% set left_group %} + + + + + + + + + {% endset %} + + {% set right_group %} + + + + + + {% endset %} + + {% endblock %} {% block body %}
    {% for manga in mangas %} -
    - +
    + {{ manga.title }} diff --git a/templates/manga/show_chapters.html.twig b/templates/manga/show_chapters.html.twig index 5e962d3..2bf8b98 100644 --- a/templates/manga/show_chapters.html.twig +++ b/templates/manga/show_chapters.html.twig @@ -1,19 +1,41 @@ {% extends 'base.html.twig' %} {% block toolbar %} -
    -
    -
    - - -
    - - -
    - - -
    -
    -
    + {% set left_group %} + + + + + + + + + {% endset %} + + {% set right_group %} + + + + + + {% endset %} + + {% endblock %} {% block body %}