From 686b614cd6ea623e5af264827f0bc32998d28568 Mon Sep 17 00:00:00 2001 From: afornerot Date: Wed, 23 Jul 2025 22:35:07 +0200 Subject: [PATCH] svg --- src/Controller/IssueController.php | 17 +++ src/Repository/IssueRepository.php | 9 +- templates/base.html.twig | 1 + templates/issue/list.html.twig | 157 +++++++++++++++++++++++ templates/project/list.html.twig | 46 +++---- templates/project/report.html.twig | 194 ++++++++++++++++++----------- 6 files changed, 326 insertions(+), 98 deletions(-) create mode 100644 templates/issue/list.html.twig diff --git a/src/Controller/IssueController.php b/src/Controller/IssueController.php index 57455b9..4bd6ec1 100644 --- a/src/Controller/IssueController.php +++ b/src/Controller/IssueController.php @@ -4,6 +4,7 @@ namespace App\Controller; use App\Form\IssueType; use App\Repository\IssueRepository; +use App\Repository\ProjectRepository; use App\Service\RedmineService; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; @@ -23,6 +24,22 @@ class IssueController extends AbstractController $this->redmineService = $redmineService; } + #[Route('/user/issue', name: 'app_issue_list')] + public function listIssues(ProjectRepository $projectRepository, IssueRepository $issueRepository): Response + { + $projects = $projectRepository->findAll(); + foreach ($projects as $project) { + $redmine = $this->redmineService->getProject($project->getId(), $this->getParameter('redmineApikey')); + $project->setRedmine($redmine); + $this->redmineService->majProjectIssues($project, $this->getParameter('redmineApikey'), false); + } + + return $this->render('issue/list.html.twig', [ + 'usemenu' => true, + 'issues' => $issueRepository->findIssues(null, false, $this->getUser()), + ]); + } + #[Route('/user/issue/{id}', name: 'app_issue_view')] public function viewIssue(int $id, IssueRepository $issueRepository): Response { diff --git a/src/Repository/IssueRepository.php b/src/Repository/IssueRepository.php index 167f0c4..cf175ad 100644 --- a/src/Repository/IssueRepository.php +++ b/src/Repository/IssueRepository.php @@ -4,6 +4,7 @@ namespace App\Repository; use App\Entity\Issue; use App\Entity\Project; +use App\Entity\User; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\Persistence\ManagerRegistry; @@ -17,13 +18,13 @@ class IssueRepository extends ServiceEntityRepository parent::__construct($registry, Issue::class); } - public function findIssues(?Project $project, bool $closed = false): array + public function findIssues(?Project $project, bool $closed = false, ?User $user = null): array { - $criteria=[]; - if($project) { + $criteria = []; + if ($project) { $criteria = ['project' => $project]; } - + if (!$closed) { $criteria['isClosed'] = false; } diff --git a/templates/base.html.twig b/templates/base.html.twig index 005e7a1..16661f2 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -59,6 +59,7 @@ {% if app.user %} {% if is_granted('ROLE_ADMIN') %} + {% endif %} diff --git a/templates/issue/list.html.twig b/templates/issue/list.html.twig new file mode 100644 index 0000000..117ed8c --- /dev/null +++ b/templates/issue/list.html.twig @@ -0,0 +1,157 @@ +{% extends 'base.html.twig' %} + +{% block localstyle %} + +{% endblock %} + +{% block title %} + = Liste des Issues +{% endblock %} + +{% block body %} +

+ Liste des Issues +

+ +
+
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+ +
+
+ + + + + + + + + + + + {% for issue in issues %} + + + + + + + + {% endfor %} + +
IdProjetNomStatutCatégorie
#{{issue.id}}{{issue.project.title}}{{issue.redmine.subject}}{{issue.redmine.status.name}}{{issue.redmine.category is defined?issue.redmine.category.name:'Aucune'}}
+
+
+
+{% endblock %} + +{% block localscript %} + +{% endblock %} diff --git a/templates/project/list.html.twig b/templates/project/list.html.twig index ae681e9..6041659 100644 --- a/templates/project/list.html.twig +++ b/templates/project/list.html.twig @@ -6,31 +6,31 @@

{{title}}

Ajouter -
- - +
+
+ + + + + + + + + + {% for project in projects %} - - - - + + + + - - - {% for project in projects %} - - - - - - - {% endfor %} - -
ActionLogoModifierNom
ActionLogoModifierNom + + + {{project.title}}{{project.redmine.updated_on}}
- - - {{project.title}}{{project.redmine.updated_on}}
-
+ {% endfor %} + + + {% endblock %} {% block localscript %} diff --git a/templates/project/report.html.twig b/templates/project/report.html.twig index 35ebc7b..358c313 100644 --- a/templates/project/report.html.twig +++ b/templates/project/report.html.twig @@ -1,57 +1,45 @@ {% extends 'base.html.twig' %} {% block body %} - {% for status in project.redmine.issue_statuses %} -
-

{{ status.name }}

+
+
+
+
+ +
- {% for sprint in project.redmine.sprints|reverse %} -
- +
+
+ +
+ + + +
- {% for version in project.redmine.versions|reverse %} - {% if version.id not in project.hiddenversions %} -
- -
-
- {% endif %} - {% endfor %} - -
- -
+
+ {% for status in project.redmine.issue_statuses %} +
+

{{ status.name }}

+
+
{% endfor %} -
- - - {% for version in project.redmine.versions|reverse %} -
- -
+ {% for issue in issues %} + {% if issue.redmine.updated_on|date('Ymd') >= date|date('Ymd') or (issue.redmine.closed_on is not null and issue.redmine.closed_on|date('Ymd') >= date|date('Ymd')) %} +
+ #{{issue.id}} = {{issue.redmine.subject}}
- {% endfor %} - -
- -
-
-
- -
+ {% endif %} + {% endfor %}
- {% endfor %} - - {% for issue in issues %} - {% if issue.redmine.updated_on|date('Ymd') >= date|date('Ymd') or (issue.redmine.closed_on is not null and issue.redmine.closed_on|date('Ymd') >= date|date('Ymd')) %} -
- #{{issue.id}} = {{issue.redmine.subject}} -
- {% endif %} - {% endfor %} +
{% endblock %} {% block javascripts %} @@ -59,7 +47,7 @@ // Ranger les issues dans status / sprint / version $(function () { $('.issueCard').each(function () { - const id = $(this).data('status')+'|'+$(this).data('sprint')+'|'+$(this).data('version'); + const id = $(this).data('status'); const $column = $(`[data-id='${id}']`); if ($column.length) { $column.append($(this)); @@ -71,39 +59,19 @@ }); $('.scrumContainer').css('display', 'flex'); - // Étape 1 : Masquer les versionCard sans enfant visible dans versionBody - document.querySelectorAll('.versionCard').forEach(card => { - const versionBody = card.querySelector('.versionBody'); - const children = Array.from(versionBody?.children || []); + // Étape 1 : Masquer les statusCard sans enfant visible dans statusBody + document.querySelectorAll('.statusCard').forEach(card => { + const statusBody = card.querySelector('.statusBody'); + const children = Array.from(statusBody?.children || []); const hasVisibleChild = children.some(child => child.offsetParent !== null); if (!hasVisibleChild) { - card.style.display = 'none'; + card.remove(); } }); - // Étape 2 : Masquer les sprintCard sans versionCard visible - document.querySelectorAll('.sprintCard').forEach(sprint => { - const visibleVersion = Array.from(sprint.querySelectorAll('.versionCard')) - .some(card => card.offsetParent !== null); - - if (!visibleVersion) { - sprint.style.display = 'none'; - } - }); - - // Étape 3 : Masquer les statusCard sans sprintCard visible - document.querySelectorAll('.statusCard').forEach(status => { - const visibleSprint = Array.from(status.querySelectorAll('.sprintCard')) - .some(sprint => sprint.offsetParent !== null); - - if (!visibleSprint) { - status.style.display = 'none'; - } - }); - - // Étape 4 : Trier les issues restantes par data-id dans chaque statusCard + // Étape 2 : Trier les issues restantes par data-id dans chaque statusCard document.querySelectorAll('.statusCard').forEach(statusCard => { const issues = Array.from(statusCard.querySelectorAll('.issueCard')); @@ -114,7 +82,7 @@ visibleIssues.sort((a, b) => { const idA = a.getAttribute('data-id'); const idB = b.getAttribute('data-id'); - return idA.localeCompare(idB, undefined, { numeric: true, sensitivity: 'base' }); + return idB.localeCompare(idA, undefined, { numeric: true, sensitivity: 'base' }); }); // Replacer les issues triées dans leur parent immédiat @@ -123,6 +91,90 @@ parent.appendChild(issue); }); }); + + // Étape 3 : Générer la liste des statusCards visibles dans le sélecteur multiple + const selector = document.getElementById('statusCardSelector'); + const statusCards = Array.from(document.querySelectorAll('.statusCard')); + + statusCards.forEach(card => { + const statusName = card.querySelector('h2')?.textContent || 'Statut inconnu'; + const statusId = card.className.match(/statusCard(\d+)/)?.[1]; + + if (statusId) { + const option = document.createElement('option'); + option.value = statusId; + option.textContent = statusName; + option.selected = card.style.display !== 'none'; // Sélectionner si visible + selector.appendChild(option); + } + }); + + // Étape 4 : Afficher/Masquer les statusCard selon les options sélectionnées + selector.addEventListener('change', function () { + const selectedValues = Array.from(this.selectedOptions).map(opt => opt.value); + + statusCards.forEach(card => { + const statusId = card.className.match(/statusCard(\d+)/)?.[1]; + if (!statusId) return; + + if (selectedValues.includes(statusId)) { + card.style.display = ''; + } else { + card.style.display = 'none'; + } + }); + }); + + // Étape 5 : Copier le contenu de .report au clic sur le bouton + document.getElementById('copyReportBtn').addEventListener('click', function () { + const reportEl = document.querySelector('.report'); + + if (!reportEl) return; + + // Créer une sélection virtuelle du contenu HTML visible + const range = document.createRange(); + range.selectNodeContents(reportEl); + + const selection = window.getSelection(); + selection.removeAllRanges(); + selection.addRange(range); + + try { + // Exécuter la commande de copie + const successful = document.execCommand('copy'); + if (successful) { + const msg = document.getElementById('copyStatusMsg'); + msg.style.display = 'inline'; + setTimeout(() => { + msg.style.display = 'none'; + }, 2000); + } else { + alert('Échec de la copie.'); + } + } catch (err) { + alert('Erreur lors de la copie : ' + err); + } + + // Nettoyer la sélection + selection.removeAllRanges(); + }); + + // Étape 6 : Redirection sur changement de mois + document.getElementById('monthSelector').addEventListener('change', function () { + const selected = this.value; // format YYYY-MM + if (!selected) return; + + const parts = selected.split('-'); + if (parts.length !== 2) return; + + const year = parts[0]; + const month = parts[1]; + const targetMonth = year + month; + + // Redirection vers la route avec le nouveau mois + window.location.href = '{{ path("app_project_report", {"month": "REPLACE_ME"}) }}'.replace('REPLACE_ME', targetMonth); + }); + }); {% endblock %}