| @ -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; | |||
| } | |||
| } | |||