This commit is contained in:
2025-07-10 22:42:04 +02:00
parent 1cc4db4943
commit ee8220f059
4 changed files with 69 additions and 58 deletions

View File

@ -69,22 +69,30 @@ class IssueController extends AbstractController
return new JsonResponse(['message' => 'Erreur Redmine : Déplacement Interdit'], 400);
}
$payload = [
'fixed_version_id' => $targetVersion,
'agile_data_attributes' => ['agile_sprint_id' => $targetSprint],
'status_id' => $targetStatus,
];
$this->redmineService->updateIssue($id, $payload, $this->getParameter('redmineApikey'));
try {
$payload = [
'fixed_version_id' => $targetVersion,
'agile_data_attributes' => ['agile_sprint_id' => $targetSprint],
'status_id' => $targetStatus,
];
$this->redmineService->updateIssue($id, $payload, $this->getUser()->getApikey());
} catch (\RuntimeException $e) {
// Récupère le message de l'exception
$errorMessage = $e->getMessage();
dump($e->getMessage());
// Par exemple, retour JSON d'erreur :
return new JsonResponse(['message' => $errorMessage], 400);
}
/*
$payload =
[
'id' => $id,
'issue' => [
'status_id' => $targetStatus,
'fixed_version_id' => $targetVersion,
],
'positions' => [],
'id' => $id,
'issue' => [
'status_id' => $targetStatus,
'fixed_version_id' => $targetVersion,
],
'positions' => [],
];
foreach ($targetIssues as $key => $issue) {

View File

@ -299,7 +299,7 @@ class RedmineService
public function getIssue(int $issueId, string $apiKey): array
{
try {
$url = "{$this->baseUrl}/issues/{$issueId}.json?include=allowed_statuses";
$url = "{$this->baseUrl}/issues/{$issueId}.json?include=allowed_statuses,journals";
$response = $this->client->request('GET', $url, [
'headers' => [
@ -345,7 +345,7 @@ class RedmineService
}
}
public function updateIssue(int $id, array $data, string $apiKey): array
public function updateIssue(int $id, array $data, ?string $apiKey): array
{
$url = $this->baseUrl.'/issues/'.$id.'.json';
@ -358,25 +358,15 @@ class RedmineService
'json' => ['issue' => $data],
]);
$statusCode = $response->getStatusCode();
$content = trim($response->getContent(false));
if (200 !== $response->getStatusCode()) {
if (401 === $response->getStatusCode()) {
throw new \RuntimeException('Erreur Redmine ('.$response->getStatusCode().') : Opération non autorisée, avez-vous placé votre apikey redmine sur votre profil');
}
// Si vide et code 200, cest peut-être une réussite silencieuse
if ('' === $content) {
return ['success' => true, 'message' => 'OK, mais pas de contenu'];
throw new \RuntimeException('Erreur de communication avec Redmine : '.$response->getStatusCode());
}
$decoded = json_decode($content, true);
if (isset($decoded['errors']) && is_array($decoded['errors']) && count($decoded['errors']) > 0) {
throw new \RuntimeException('Erreur Redmine : '.implode(', ', $decoded['errors']));
}
return $decoded;
} catch (ClientExceptionInterface|ServerExceptionInterface $e) {
// Erreur HTTP (4xx ou 5xx)
$errorBody = $e->getResponse()->getContent(false);
throw new \RuntimeException('Erreur Redmine: '.$errorBody, $e->getCode(), $e);
return $response->toArray();
} catch (TransportExceptionInterface $e) {
throw new \RuntimeException('Erreur de communication avec Redmine : '.$e->getMessage());
}

View File

@ -6,7 +6,15 @@
<div class="btn btn-secondary" onClick="hideIssue()"><i class="fas fa-window-close"></i></div>
</div>
<small class="text-muted">Projet : {{ issue.redmine.project.name }} • Tracker : {{ issue.redmine.tracker.name }}</small>
<div class="d-flex">
<small class="text-muted" style="flex-grow:1">Projet : {{ issue.redmine.project.name }} • Tracker : {{ issue.redmine.tracker.name }}</small>
{% for field in issue.redmine.custom_fields %}
{% if field.id==11 and field.value!="" %}
<small class="text-muted"><strong>{{ field.name }} =</strong> <a href="{{field.value}}" target="_blank">{{field.value|split('/')|last}}</a></small>
{% endif %}
{% endfor %}
</div>
</div>
<div class="issueDescription card-body" style="height:500px;overflow-y:auto">
@ -40,40 +48,43 @@
<strong>Date de début =</strong> {{ issue.redmine.start_date|date('d/m/Y') }}<br>
<strong>Date de fin =</strong> {{ issue.redmine.due_date|date('d/m/Y') }}<br><br>
<strong>Affecté à =</strong> {{(issue.redmine.assigned_to is defined?issue.redmine.assigned_to.name:'')}}<br>
<strong>Affecté à =</strong> {{(issue.redmine.assigned_to is defined?issue.redmine.assigned_to.name:'')}}<br>
</div>
</div>
<div>
{% for field in issue.redmine.custom_fields %}
{% if field.id==32 %}
<hr>
<strong>{{ field.name }} </strong><br>
{{ field.value|textile_to_html|raw }}<br>
{% endif %}
{% endfor %}
</div>
{% if issue.redmine.description %}
<div class="mb-3">
<hr>
<strong>Description :</strong>
<p>{{ issue.redmine.description|textile_to_html|raw }}</p>
<hr>
</div>
{% endif %}
{% if issue.redmine.custom_fields|length %}
<div class="mb-3">
<hr>
<strong>Champs personnalisés :</strong>
<ul class="list-group">
{% for field in issue.redmine.custom_fields %}
<li class="list-group-item d-flex justify-content-between align-items-center">
{{ field.name }}
<span class="text-muted">
{% if field.multiple is defined and field.multiple and field.value is iterable %}
{{ field.value|join(', ') }}
{% elseif field.value %}
{{ field.value }}
{% else %}
<em>—</em>
{% endif %}
</span>
</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% for journal in issue.redmine.journals %}
{% if journal.notes != "" %}
<div class="card">
<div class="card-header">
<strong>Auteur =</strong> {{journal.user.name}}</strong><br>
<small class="text-muted">Créé le =</strong>{{ journal.created_on|date('d/m/Y H:i') }}</small>
</div>
<div class="card-body">
{{ journal.notes |textile_to_html|raw }}
</div>
</div>
{% endif %}
{% endfor %}
<hr>
{{dump(issue)}}
<br><br>

View File

@ -192,8 +192,9 @@
<div class='filtreContainer'>
<label>Issue</label>
<input type="number" id="issueSearchInput" class="form-control" placeholder="Rechercher une issue" />
<label>Statut</label>
<a href="{{redmineUrl}}/projects/{{project.id}}/issues/new" target="_blank" class="btn btn-primary btn-sm"><i class="fas fa-file"></i> Nouvelle Demande</a>
<label style="margin-top:30px">Statut</label>
<select id="statusFilter" class="select2 form-select" multiple="true" tabindex="-1" aria-hidden="true">
{% for statut in project.redmine.issue_statuses %}
{% if statut.id not in project.hiddenstatuses %}
@ -381,9 +382,10 @@
});
// Scroll horizontal (retourne une promesse)
let paddingLeft = parseInt($('.scrumContainer').css('padding-left'), 315);
const scrollLeftPromise = new Promise(resolve => {
$('html, body').animate({
scrollLeft: $element.offset().left - 315
scrollLeft: $element.offset().left - paddingLeft
}, 500, resolve);
});
@ -530,7 +532,7 @@
localStorage.removeItem(viewedIssueKey);
$('.issueContainer').hide();
$('.scrumContainer').css('padding-left','0px');
$('.scrumContainer').css('padding-left','300px');
}
// Filtre sur les issues et backup des filtres en localstorage