mirror of
https://github.com/MarijnDoeve/TijdVoorDeTest.git
synced 2026-03-05 20:44:19 +01:00
Add quiz clearing and deletion functionality with UI enhancements
This commit introduces the ability to clear quiz results and delete quizzes directly from the backoffice. It includes new routes, controllers, modals for user confirmation, and updates to translations. The `QuizRepository` now supports dedicated methods for clearing results and deleting quizzes along with error handling. Related database migrations and front-end adjustments are also included.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import * as bootstrap from 'bootstrap'
|
||||
import './bootstrap.js';
|
||||
import 'bootstrap/dist/css/bootstrap.min.css'
|
||||
import * as bootstrap from 'bootstrap'
|
||||
|
||||
import './styles/backoffice.scss';
|
||||
|
||||
17
assets/controllers/bo/quiz_controller.js
Normal file
17
assets/controllers/bo/quiz_controller.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import {Controller} from '@hotwired/stimulus';
|
||||
import * as bootstrap from 'bootstrap'
|
||||
|
||||
export default class extends Controller {
|
||||
connect() {
|
||||
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]')
|
||||
const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl))
|
||||
}
|
||||
|
||||
clearQuiz() {
|
||||
new bootstrap.Modal('#clearQuizModal').show();
|
||||
}
|
||||
|
||||
deleteQuiz() {
|
||||
new bootstrap.Modal('#deleteQuizModal').show();
|
||||
}
|
||||
}
|
||||
41
migrations/Version20250607154730.php
Normal file
41
migrations/Version20250607154730.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20250607154730 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql(<<<'SQL'
|
||||
ALTER TABLE season DROP CONSTRAINT FK_F0E45BA96706D6B
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
ALTER TABLE season ADD CONSTRAINT FK_F0E45BA96706D6B FOREIGN KEY (active_quiz_id) REFERENCES quiz (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE
|
||||
SQL);
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql(<<<'SQL'
|
||||
ALTER TABLE season DROP CONSTRAINT fk_f0e45ba96706d6b
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
ALTER TABLE season ADD CONSTRAINT fk_f0e45ba96706d6b FOREIGN KEY (active_quiz_id) REFERENCES quiz (id) NOT DEFERRABLE INITIALLY IMMEDIATE
|
||||
SQL);
|
||||
}
|
||||
}
|
||||
53
migrations/Version20250607184525.php
Normal file
53
migrations/Version20250607184525.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20250607184525 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql(<<<'SQL'
|
||||
ALTER TABLE elimination DROP CONSTRAINT FK_5947284F853CD175
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
ALTER TABLE elimination ADD CONSTRAINT FK_5947284F853CD175 FOREIGN KEY (quiz_id) REFERENCES quiz (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
ALTER TABLE given_answer DROP CONSTRAINT FK_9AC61A30853CD175
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
ALTER TABLE given_answer ADD CONSTRAINT FK_9AC61A30853CD175 FOREIGN KEY (quiz_id) REFERENCES quiz (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE
|
||||
SQL);
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql(<<<'SQL'
|
||||
ALTER TABLE given_answer DROP CONSTRAINT fk_9ac61a30853cd175
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
ALTER TABLE given_answer ADD CONSTRAINT fk_9ac61a30853cd175 FOREIGN KEY (quiz_id) REFERENCES quiz (id) NOT DEFERRABLE INITIALLY IMMEDIATE
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
ALTER TABLE elimination DROP CONSTRAINT fk_5947284f853cd175
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
ALTER TABLE elimination ADD CONSTRAINT fk_5947284f853cd175 FOREIGN KEY (quiz_id) REFERENCES quiz (id) NOT DEFERRABLE INITIALLY IMMEDIATE
|
||||
SQL);
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ return RectorConfig::configure()
|
||||
phpunitCodeQuality: true,
|
||||
doctrineCodeQuality: true,
|
||||
symfonyCodeQuality: true,
|
||||
// naming: true
|
||||
)
|
||||
->withAttributesSets(all: true)
|
||||
->withComposerBased(twig: true, doctrine: true, phpunit: true, symfony: true)
|
||||
|
||||
@@ -10,6 +10,7 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController as AbstractBase
|
||||
abstract class AbstractController extends AbstractBaseController
|
||||
{
|
||||
protected const string SEASON_CODE_REGEX = '[A-Za-z\d]{5}';
|
||||
|
||||
protected const string CANDIDATE_HASH_REGEX = '[\w\-=]+';
|
||||
|
||||
#[\Override]
|
||||
|
||||
@@ -39,9 +39,10 @@ final class PrepareEliminationController extends AbstractController
|
||||
$elimination->updateFromInputBag($request->request);
|
||||
$em->flush();
|
||||
|
||||
if (true === $request->request->getBoolean('start')) {
|
||||
if ($request->request->getBoolean('start')) {
|
||||
return $this->redirectToRoute('app_elimination', ['elimination' => $elimination->getId()]);
|
||||
}
|
||||
|
||||
$this->addFlash('success', 'Elimination updated');
|
||||
}
|
||||
|
||||
|
||||
@@ -8,8 +8,10 @@ use App\Controller\AbstractController;
|
||||
use App\Entity\Candidate;
|
||||
use App\Entity\Quiz;
|
||||
use App\Entity\Season;
|
||||
use App\Exception\ErrorClearingQuizException;
|
||||
use App\Repository\CandidateRepository;
|
||||
use App\Repository\QuizCandidateRepository;
|
||||
use App\Repository\QuizRepository;
|
||||
use App\Security\Voter\SeasonVoter;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
@@ -19,6 +21,7 @@ use Symfony\Component\HttpKernel\Attribute\AsController;
|
||||
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
#[AsController]
|
||||
#[IsGranted('ROLE_USER')]
|
||||
@@ -26,6 +29,7 @@ class QuizController extends AbstractController
|
||||
{
|
||||
public function __construct(
|
||||
private readonly CandidateRepository $candidateRepository,
|
||||
private readonly TranslatorInterface $translator,
|
||||
) {}
|
||||
|
||||
#[Route(
|
||||
@@ -61,6 +65,37 @@ class QuizController extends AbstractController
|
||||
return $this->redirectToRoute('app_backoffice_season', ['seasonCode' => $season->getSeasonCode()]);
|
||||
}
|
||||
|
||||
#[Route(
|
||||
'/backoffice/quiz/{quiz}/clear',
|
||||
name: 'app_backoffice_quiz_clear',
|
||||
)]
|
||||
#[IsGranted(SeasonVoter::EDIT, subject: 'quiz')]
|
||||
public function clearQuiz(Quiz $quiz, QuizRepository $quizRepository): RedirectResponse
|
||||
{
|
||||
try {
|
||||
$quizRepository->clearQuiz($quiz);
|
||||
$this->addFlash('success', $this->translator->trans('Quiz cleared'));
|
||||
} catch (ErrorClearingQuizException) {
|
||||
$this->addFlash('error', $this->translator->trans('Error clearing quiz'));
|
||||
}
|
||||
|
||||
return $this->redirectToRoute('app_backoffice_quiz', ['seasonCode' => $quiz->getSeason()->getSeasonCode(), 'quiz' => $quiz->getId()]);
|
||||
}
|
||||
|
||||
#[Route(
|
||||
'/backoffice/quiz/{quiz}/delete',
|
||||
name: 'app_backoffice_quiz_delete',
|
||||
)]
|
||||
#[IsGranted(SeasonVoter::DELETE, subject: 'quiz')]
|
||||
public function deleteQuiz(Quiz $quiz, QuizRepository $quizRepository): RedirectResponse
|
||||
{
|
||||
$quizRepository->deleteQuiz($quiz);
|
||||
|
||||
$this->addFlash('success', $this->translator->trans('Quiz deleted'));
|
||||
|
||||
return $this->redirectToRoute('app_backoffice_season', ['seasonCode' => $quiz->getSeason()->getSeasonCode()]);
|
||||
}
|
||||
|
||||
#[Route(
|
||||
'/backoffice/quiz/{quiz}/modify_correction/{candidate}',
|
||||
name: 'app_backoffice_modify_correction',
|
||||
|
||||
@@ -26,7 +26,7 @@ use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
#[IsGranted('ROLE_USER')]
|
||||
class SeasonController extends AbstractController
|
||||
{
|
||||
public function __construct(private readonly TranslatorInterface $translator, private EntityManagerInterface $em,
|
||||
public function __construct(private readonly TranslatorInterface $translator, private readonly EntityManagerInterface $em,
|
||||
) {}
|
||||
|
||||
#[Route(
|
||||
|
||||
@@ -18,6 +18,7 @@ use Symfony\Component\Uid\Uuid;
|
||||
class Elimination
|
||||
{
|
||||
public const string SCREEN_GREEN = 'green';
|
||||
|
||||
public const string SCREEN_RED = 'red';
|
||||
|
||||
#[ORM\Id]
|
||||
@@ -35,7 +36,7 @@ class Elimination
|
||||
|
||||
public function __construct(
|
||||
#[ORM\ManyToOne(inversedBy: 'eliminations')]
|
||||
#[ORM\JoinColumn(nullable: false)]
|
||||
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
|
||||
private Quiz $quiz,
|
||||
) {}
|
||||
|
||||
@@ -66,7 +67,7 @@ class Elimination
|
||||
/** @param InputBag<bool|float|int|string> $inputBag */
|
||||
public function updateFromInputBag(InputBag $inputBag): self
|
||||
{
|
||||
foreach ($this->data as $name => $screenColour) {
|
||||
foreach (array_keys($this->data) as $name) {
|
||||
$newColour = $inputBag->get('colour-'.mb_strtolower($name));
|
||||
if (\is_string($newColour)) {
|
||||
$this->data[$name] = $inputBag->get('colour-'.mb_strtolower($name));
|
||||
|
||||
@@ -31,7 +31,7 @@ class GivenAnswer
|
||||
private Candidate $candidate,
|
||||
|
||||
#[ORM\ManyToOne]
|
||||
#[ORM\JoinColumn(nullable: false)]
|
||||
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
|
||||
private Quiz $quiz,
|
||||
|
||||
#[ORM\ManyToOne(inversedBy: 'givenAnswers')]
|
||||
|
||||
@@ -43,6 +43,7 @@ class Season
|
||||
private Collection $owners;
|
||||
|
||||
#[ORM\ManyToOne]
|
||||
#[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]
|
||||
private ?Quiz $ActiveQuiz = null;
|
||||
|
||||
public function __construct()
|
||||
|
||||
7
src/Exception/ErrorClearingQuizException.php
Normal file
7
src/Exception/ErrorClearingQuizException.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Exception;
|
||||
|
||||
class ErrorClearingQuizException extends \Exception {}
|
||||
@@ -4,17 +4,59 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\Elimination;
|
||||
use App\Entity\GivenAnswer;
|
||||
use App\Entity\Quiz;
|
||||
use App\Entity\QuizCandidate;
|
||||
use App\Exception\ErrorClearingQuizException;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<Quiz>
|
||||
*/
|
||||
class QuizRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
public function __construct(ManagerRegistry $registry, private readonly LoggerInterface $logger)
|
||||
{
|
||||
parent::__construct($registry, Quiz::class);
|
||||
}
|
||||
|
||||
/** @throws ErrorClearingQuizException */
|
||||
public function clearQuiz(Quiz $quiz): void
|
||||
{
|
||||
$em = $this->getEntityManager();
|
||||
$em->beginTransaction();
|
||||
try {
|
||||
$em->createQueryBuilder()
|
||||
->delete()->from(QuizCandidate::class, 'qc')
|
||||
->where('qc.quiz = :quiz')
|
||||
->setParameter('quiz', $quiz)
|
||||
->getQuery()->execute();
|
||||
|
||||
$em->createQueryBuilder()
|
||||
->delete()->from(GivenAnswer::class, 'ga')
|
||||
->where('ga.quiz = :quiz')
|
||||
->setParameter('quiz', $quiz)
|
||||
->getQuery()->execute();
|
||||
$em->createQueryBuilder()
|
||||
->delete()->from(Elimination::class, 'e')
|
||||
->where('e.quiz = :quiz')
|
||||
->setParameter('quiz', $quiz)
|
||||
->getQuery()->execute();
|
||||
} catch (\Throwable $throwable) {
|
||||
$this->logger->error($throwable->getMessage());
|
||||
$em->rollback();
|
||||
throw new ErrorClearingQuizException(previous: $throwable);
|
||||
}
|
||||
|
||||
$em->commit();
|
||||
}
|
||||
|
||||
public function deleteQuiz(Quiz $quiz): void
|
||||
{
|
||||
$this->getEntityManager()->remove($quiz);
|
||||
$this->getEntityManager()->flush();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,43 +11,45 @@
|
||||
{{ 'Add'|trans }}
|
||||
</a>
|
||||
</div>
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
{% if is_granted('ROLE_ADMIN') %}
|
||||
<th scope="col">{{ 'Owner(s)'|trans }}</th>
|
||||
{% endif %}
|
||||
<th scope="col">{{ 'Name'|trans }}</th>
|
||||
<th scope="col">{{ 'Active Quiz'|trans }}</th>
|
||||
<th scope="col">{{ 'Season Code'|trans }}</th>
|
||||
<th scope="col">{{ 'Manage'|trans }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for season in seasons %}
|
||||
<tr class="align-middle">
|
||||
{% if seasons %}
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
{% if is_granted('ROLE_ADMIN') %}
|
||||
<td>{{ season.owners|map(o => o.email)|join(', ') }}</td>
|
||||
<th scope="col">{{ 'Owner(s)'|trans }}</th>
|
||||
{% endif %}
|
||||
<td>{{ season.name }}</td>
|
||||
<td>
|
||||
{% if season.activeQuiz %}
|
||||
{{ season.activeQuiz.name }}
|
||||
{% else %}
|
||||
{{ 'No active quiz'|trans }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<a {% if season.activeQuiz %}href="{{ path('app_quiz_enter_name', {seasonCode: season.seasonCode}) }}"
|
||||
{% else %}class="disabled" {% endif %}>{{ season.seasonCode }}</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ path('app_backoffice_season', {seasonCode: season.seasonCode}) }}">{{ 'Manage'|trans }}</a>
|
||||
</td>
|
||||
<th scope="col">{{ 'Name'|trans }}</th>
|
||||
<th scope="col">{{ 'Active Quiz'|trans }}</th>
|
||||
<th scope="col">{{ 'Season Code'|trans }}</th>
|
||||
<th scope="col">{{ 'Manage'|trans }}</th>
|
||||
</tr>
|
||||
{% else %}
|
||||
EMPTY
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for season in seasons %}
|
||||
<tr class="align-middle">
|
||||
{% if is_granted('ROLE_ADMIN') %}
|
||||
<td>{{ season.owners|map(o => o.email)|join(', ') }}</td>
|
||||
{% endif %}
|
||||
<td>{{ season.name }}</td>
|
||||
<td>
|
||||
{% if season.activeQuiz %}
|
||||
{{ season.activeQuiz.name }}
|
||||
{% else %}
|
||||
{{ 'No active quiz'|trans }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<a {% if season.activeQuiz %}href="{{ path('app_quiz_enter_name', {seasonCode: season.seasonCode}) }}"
|
||||
{% else %}class="disabled" {% endif %}>{{ season.seasonCode }}</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ path('app_backoffice_season', {seasonCode: season.seasonCode}) }}">{{ 'Manage'|trans }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
{{ 'You have no seasons yet.'|trans }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -2,13 +2,19 @@
|
||||
|
||||
{% block body %}
|
||||
<h2 class="py-2">{{ 'Quiz'|trans }}: {{ quiz.season.name }} - {{ quiz.name }}</h2>
|
||||
<div class="py-2 btn-group">
|
||||
<div class="py-2 btn-group" data-controller="bo--quiz">
|
||||
<a class="btn btn-primary {% if quiz is same as(season.activeQuiz) %}disabled{% endif %}"
|
||||
href="{{ path('app_backoffice_enable', {seasonCode: season.seasonCode, quiz: quiz.id}) }}">{{ 'Make active'|trans }}</a>
|
||||
{% if quiz is same as (season.activeQuiz) %}
|
||||
<a class="btn btn-secondary"
|
||||
href="{{ path('app_backoffice_enable', {seasonCode: season.seasonCode, quiz: 'null'}) }}">{{ 'Deactivate Quiz'|trans }}</a>
|
||||
{% endif %}
|
||||
<button class="btn btn-danger" data-action="click->bo--quiz#clearQuiz">
|
||||
{{ 'Clear quiz...'|trans }}
|
||||
</button>
|
||||
<button class="btn btn-danger" data-action="click->bo--quiz#deleteQuiz">
|
||||
{{ 'Delete Quiz...'|trans }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="questions">
|
||||
@@ -111,16 +117,48 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block javascripts %}
|
||||
{{ parent() }}
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]')
|
||||
const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl))
|
||||
});
|
||||
</script>
|
||||
{% endblock javascripts %}
|
||||
{% block title %}
|
||||
|
||||
{# Modal Clear #}
|
||||
<div class="modal fade" id="clearQuizModal" data-bs-backdrop="static"
|
||||
tabindex="-1"
|
||||
aria-labelledby="staticBackdropLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5" id="staticBackdropLabel">Please Confirm</h1>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
Are you sure you want to clear all the results? This will also delete al the eliminations.
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">No</button>
|
||||
<a href="{{ path('app_backoffice_quiz_clear', {quiz: quiz.id}) }}" class="btn btn-danger">Yes</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Modal Delete #}
|
||||
<div class="modal fade" id="deleteQuizModal" data-bs-backdrop="static"
|
||||
tabindex="-1"
|
||||
aria-labelledby="staticBackdropLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5" id="staticBackdropLabel">Please Confirm</h1>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
Are you sure you want to delete this quiz?
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">No</button>
|
||||
<a href="{{ path('app_backoffice_quiz_delete', {quiz: quiz.id}) }}" class="btn btn-danger">Yes</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block title %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -18,19 +18,19 @@ use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
|
||||
|
||||
class SeasonVoterTest extends TestCase
|
||||
final class SeasonVoterTest extends TestCase
|
||||
{
|
||||
private SeasonVoter $seasonVoter;
|
||||
|
||||
private TokenInterface&Stub $token;
|
||||
private User&Stub $user;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->seasonVoter = new SeasonVoter();
|
||||
$this->token = $this->createStub(TokenInterface::class);
|
||||
|
||||
$this->user = $this->createStub(User::class);
|
||||
$this->token->method('getUser')->willReturn($this->user);
|
||||
$user = $this->createStub(User::class);
|
||||
$this->token->method('getUser')->willReturn($user);
|
||||
}
|
||||
|
||||
#[DataProvider('typesProvider')]
|
||||
|
||||
@@ -49,6 +49,10 @@
|
||||
<source>Candidates</source>
|
||||
<target>Kandidaten</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="FNY513f" resname="Clear quiz...">
|
||||
<source>Clear quiz...</source>
|
||||
<target>Test leegmaken...</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sFpB4C2" resname="Correct Answers">
|
||||
<source>Correct Answers</source>
|
||||
<target>Goede antwoorden</target>
|
||||
@@ -77,6 +81,10 @@
|
||||
<source>Deactivate Quiz</source>
|
||||
<target>Deactiveer test</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="p9GNNI3" resname="Delete Quiz...">
|
||||
<source>Delete Quiz...</source>
|
||||
<target>Test verwijderen...</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="R9yHzHv" resname="Download Template">
|
||||
<source>Download Template</source>
|
||||
<target>Download sjabloon</target>
|
||||
@@ -93,6 +101,10 @@
|
||||
<source>Enter your name</source>
|
||||
<target>Voor je naam in</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="HNMwvRn" resname="Error clearing quiz">
|
||||
<source>Error clearing quiz</source>
|
||||
<target>Fout bij leegmaken test</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="OGiIhMH" resname="Green">
|
||||
<source>Green</source>
|
||||
<target>Groen</target>
|
||||
@@ -177,10 +189,18 @@
|
||||
<source>Quiz Added!</source>
|
||||
<target>Test toegevoegd!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="vXN8b2w" resname="Quiz cleared">
|
||||
<source>Quiz cleared</source>
|
||||
<target>Test leeggemaakt</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="LbVe.2c" resname="Quiz completed">
|
||||
<source>Quiz completed</source>
|
||||
<target>Test voltooid</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="XdfTTMD" resname="Quiz deleted">
|
||||
<source>Quiz deleted</source>
|
||||
<target>Test verwijderd</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="frxoIkW" resname="Quiz name">
|
||||
<source>Quiz name</source>
|
||||
<target>Testnaam</target>
|
||||
@@ -237,10 +257,6 @@
|
||||
<source>Sign in</source>
|
||||
<target>Log in</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="2QO7aYC" resname="Start Elimination">
|
||||
<source>Start Elimination</source>
|
||||
<target>Start eliminatie</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="9m8DOBg" resname="Submit">
|
||||
<source>Submit</source>
|
||||
<target>Verstuur</target>
|
||||
@@ -253,10 +269,18 @@
|
||||
<source>There are no answers for this question</source>
|
||||
<target>Er zijn geen antwoorden voor deze vraag</target>
|
||||
</trans-unit>
|
||||
<trans-unit id=".LrcTyU" resname="There is no active quiz">
|
||||
<source>There is no active quiz</source>
|
||||
<target>Er is geen test actief</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Dptvysv" resname="Time">
|
||||
<source>Time</source>
|
||||
<target>Tijd</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="0afY1NF" resname="You have no seasons yet.">
|
||||
<source>You have no seasons yet.</source>
|
||||
<target>Je hebt nog geen seizoenen.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="vVQAP9A" resname="Your Seasons">
|
||||
<source>Your Seasons</source>
|
||||
<target>Jouw seizoenen</target>
|
||||
|
||||
Reference in New Issue
Block a user