|
|
@ -0,0 +1,134 @@ |
|
|
|
<?php |
|
|
|
|
|
|
|
namespace App\Controller; |
|
|
|
|
|
|
|
use App\Entity\Project; |
|
|
|
use App\Entity\Task; |
|
|
|
use App\Service\TaskLifecycleManager; |
|
|
|
use Doctrine\ORM\EntityManagerInterface; |
|
|
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; |
|
|
|
use Symfony\Component\HttpFoundation\Response; |
|
|
|
use Symfony\Component\HttpFoundation\StreamedResponse; |
|
|
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; |
|
|
|
use Symfony\Component\Routing\Attribute\Route; |
|
|
|
|
|
|
|
#[Route('/badge')]
|
|
|
|
class BadgeController extends AbstractController |
|
|
|
{ |
|
|
|
#[Route('/project-status/{projectSlug}/{status}', name: 'app_badge_project_status')]
|
|
|
|
public function index(EntityManagerInterface $entityManager, TaskLifecycleManager $taskLifecycleManager, string $projectSlug, string $status): Response |
|
|
|
{ |
|
|
|
$project = $entityManager->getRepository(Project::class)->findOneBySlug($projectSlug); |
|
|
|
if (!$project) { |
|
|
|
throw new NotFoundHttpException('Project not found'); |
|
|
|
} |
|
|
|
|
|
|
|
$stats = $taskLifecycleManager->getProjectStats($project); |
|
|
|
if (!isset($stats[$status])) { |
|
|
|
throw new NotFoundHttpException('Status not found'); |
|
|
|
} |
|
|
|
|
|
|
|
$stats = $stats[$status]; |
|
|
|
|
|
|
|
$response = new StreamedResponse(); |
|
|
|
|
|
|
|
$response->headers->set('Content-Type', 'image/svg+xml'); |
|
|
|
$response->headers->set('Cache-Control', 'public, max-age=300'); |
|
|
|
|
|
|
|
$response->setCallback(function () use ($stats): void { |
|
|
|
$colors = [ |
|
|
|
'primary' => '#0d6efd', // $blue;
|
|
|
|
'secondary' => '#6c757d', // $gray-600;
|
|
|
|
'success' => '#198754', // $green;
|
|
|
|
'info' => '#0dcaf0', // $cyan;
|
|
|
|
'warning' => '#ffc107', // $yellow;
|
|
|
|
'danger' => '#dc3545', // $red;
|
|
|
|
]; |
|
|
|
|
|
|
|
$xml = new \DOMDocument(); |
|
|
|
|
|
|
|
$left = $stats['title']; |
|
|
|
$leftColor = $colors['secondary']; |
|
|
|
$right = sprintf(' % 3.0f%%', $stats['percentage']); |
|
|
|
$rightColor = $colors[$stats['color']]; |
|
|
|
|
|
|
|
$charWidth = 7; |
|
|
|
$fontSize = 11; |
|
|
|
$totalHeight = 20; |
|
|
|
$radius = ceil($totalHeight / $charWidth); |
|
|
|
$verticalMargin = round(($totalHeight - $fontSize) / 2); |
|
|
|
$horizontalMargin = round($fontSize / 3); |
|
|
|
$totalWidth = round(strlen($left.$right) * $charWidth + 2 * $horizontalMargin); |
|
|
|
$middle = floor(strlen($left) * $charWidth); |
|
|
|
|
|
|
|
$svg = $xml->createElement('svg'); |
|
|
|
$svg->setAttributeNS('http://www.w3.org/2000/xmlns/','xmlns', 'http://www.w3.org/2000/svg'); |
|
|
|
$svg->setAttribute('width', $totalWidth); |
|
|
|
$svg->setAttribute('height', $totalHeight); |
|
|
|
|
|
|
|
$defs = $xml->createElement('defs'); |
|
|
|
|
|
|
|
$gradientId = 'a'; |
|
|
|
$gradient = $xml->createElement('linearGradient'); |
|
|
|
$gradient->setAttribute('id', $gradientId); |
|
|
|
|
|
|
|
$stop = $xml->createElement('stop'); |
|
|
|
$stop->setAttribute('offset', ($middle * 100 / $totalWidth).'%'); |
|
|
|
$stop->setAttribute('stop-color', $leftColor); |
|
|
|
$gradient->appendChild($stop); |
|
|
|
|
|
|
|
$stop = $xml->createElement('stop'); |
|
|
|
$stop->setAttribute('offset', ($middle * 100 / $totalWidth).'%'); |
|
|
|
$stop->setAttribute('stop-color', $rightColor); |
|
|
|
$gradient->appendChild($stop); |
|
|
|
|
|
|
|
$defs->appendChild($gradient); |
|
|
|
|
|
|
|
$svg->appendChild($defs); |
|
|
|
|
|
|
|
$rect = $xml->createElement('rect'); |
|
|
|
$rect->setAttribute('width', $totalWidth); |
|
|
|
$rect->setAttribute('height', $totalHeight); |
|
|
|
$rect->setAttribute('rx', $radius); |
|
|
|
$rect->setAttribute('style', sprintf('fill:url(#%s)', $gradientId)); |
|
|
|
|
|
|
|
$svg->appendChild($rect); |
|
|
|
|
|
|
|
$fontG = $xml->createElement('g'); |
|
|
|
$fontG->setAttribute('font-family', implode(',', ['DejaVu Sans', 'Verdana', 'Geneva', 'sans-serif'])); |
|
|
|
$fontG->setAttribute('font-size', $fontSize); |
|
|
|
|
|
|
|
$leftText = $xml->createElement('text', $left); |
|
|
|
$leftText->setAttribute('x', $horizontalMargin); |
|
|
|
$leftText->setAttribute('y', $totalHeight - $verticalMargin); |
|
|
|
$leftText->setAttribute('fill', '#000'); |
|
|
|
$fontG->appendChild($leftText); |
|
|
|
|
|
|
|
$leftText = $xml->createElement('text', $left); |
|
|
|
$leftText->setAttribute('x', $horizontalMargin); |
|
|
|
$leftText->setAttribute('y', $totalHeight - $verticalMargin - 1); |
|
|
|
$leftText->setAttribute('fill', '#fff'); |
|
|
|
$fontG->appendChild($leftText); |
|
|
|
|
|
|
|
$rightText = $xml->createElement('text', $right); |
|
|
|
$rightText->setAttribute('x', $totalWidth - $horizontalMargin); |
|
|
|
$rightText->setAttribute('y', $totalHeight - $verticalMargin); |
|
|
|
$rightText->setAttribute('style', 'text-anchor:end;fill:#000'); |
|
|
|
$fontG->appendChild($rightText); |
|
|
|
|
|
|
|
$rightText = $xml->createElement('text', $right); |
|
|
|
$rightText->setAttribute('x', $totalWidth - $horizontalMargin); |
|
|
|
$rightText->setAttribute('y', $totalHeight - $verticalMargin - 1); |
|
|
|
$rightText->setAttribute('style', 'text-anchor:end;fill:#fff'); |
|
|
|
$fontG->appendChild($rightText); |
|
|
|
|
|
|
|
$svg->appendChild($fontG); |
|
|
|
|
|
|
|
$xml->appendChild($svg); |
|
|
|
|
|
|
|
echo $xml->saveXML(); |
|
|
|
}); |
|
|
|
return $response; |
|
|
|
} |
|
|
|
|
|
|
|
} |