mirror of
https://github.com/MarijnDoeve/TijdVoorDeTest.git
synced 2026-07-07 16:10:15 +02:00
Refactors and start of candidate answer relation
This commit is contained in:
Generated
+5
@@ -6,6 +6,11 @@
|
|||||||
<synchronize>true</synchronize>
|
<synchronize>true</synchronize>
|
||||||
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
|
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
|
||||||
<jdbc-url>jdbc:postgresql://localhost:5432/app</jdbc-url>
|
<jdbc-url>jdbc:postgresql://localhost:5432/app</jdbc-url>
|
||||||
|
<jdbc-additional-properties>
|
||||||
|
<property name="com.intellij.clouds.kubernetes.db.host.port" />
|
||||||
|
<property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />
|
||||||
|
<property name="com.intellij.clouds.kubernetes.db.container.port" />
|
||||||
|
</jdbc-additional-properties>
|
||||||
<working-dir>$ProjectFileDir$</working-dir>
|
<working-dir>$ProjectFileDir$</working-dir>
|
||||||
</data-source>
|
</data-source>
|
||||||
</component>
|
</component>
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import * as bootstrap from 'bootstrap'
|
import './stimulus.js';
|
||||||
import './bootstrap.js';
|
import './bootstrap.js'
|
||||||
import 'bootstrap/dist/css/bootstrap.min.css'
|
|
||||||
|
|
||||||
import './styles/backoffice.scss';
|
import './styles/backoffice.scss';
|
||||||
|
|||||||
Vendored
+2
-5
@@ -1,5 +1,2 @@
|
|||||||
import { startStimulusApp } from '@symfony/stimulus-bundle';
|
import * as bootstrap from 'bootstrap'
|
||||||
|
import 'bootstrap/dist/css/bootstrap.min.css'
|
||||||
const app = startStimulusApp();
|
|
||||||
// register any custom, 3rd party controllers here
|
|
||||||
// app.register('some_controller_name', SomeImportedController);
|
|
||||||
|
|||||||
+3
-5
@@ -1,6 +1,4 @@
|
|||||||
import './bootstrap.js';
|
import './stimulus.js';
|
||||||
import 'bootstrap/dist/css/bootstrap.min.css'
|
import './bootstrap.js'
|
||||||
import * as bootstrap from 'bootstrap'
|
import './styles/quiz.scss'
|
||||||
|
|
||||||
import './styles/app.scss'
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import { startStimulusApp } from '@symfony/stimulus-bundle';
|
||||||
|
|
||||||
|
const app = startStimulusApp();
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
// register any custom, 3rd party controllers here
|
|
||||||
// app.register('some_controller_name', SomeImportedController);
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# yaml-language-server: $schema=https://raw.githubusercontent.com/compose-spec/compose-go/master/schema/compose-spec.json
|
||||||
services:
|
services:
|
||||||
php:
|
php:
|
||||||
build:
|
build:
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# yaml-language-server: $schema=https://raw.githubusercontent.com/compose-spec/compose-go/master/schema/compose-spec.json
|
||||||
# Development environment override
|
# Development environment override
|
||||||
services:
|
services:
|
||||||
php:
|
php:
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# yaml-language-server: $schema=https://raw.githubusercontent.com/compose-spec/compose-go/master/schema/compose-spec.json
|
||||||
# Production environment override
|
# Production environment override
|
||||||
services:
|
services:
|
||||||
php:
|
php:
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# yaml-language-server: $schema=https://raw.githubusercontent.com/compose-spec/compose-go/master/schema/compose-spec.json
|
||||||
services:
|
services:
|
||||||
php:
|
php:
|
||||||
image: ${IMAGES_PREFIX:-}app-php
|
image: ${IMAGES_PREFIX:-}app-php
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
# yaml-language-server: $schema=../../vendor/symfony/dependency-injection/Loader/schema/services.schema.json
|
||||||
|
when@dev:
|
||||||
|
maker:
|
||||||
|
root_namespace: 'Tvdt'
|
||||||
|
generate_final_classes: true
|
||||||
|
generate_final_entities: false
|
||||||
@@ -5,12 +5,10 @@ security:
|
|||||||
|
|
||||||
# https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
|
# https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
|
||||||
providers:
|
providers:
|
||||||
# used to reload user from session & other features (e.g. switch_user)
|
|
||||||
tvdt_user_provider:
|
tvdt_user_provider:
|
||||||
entity:
|
entity:
|
||||||
class: Tvdt\Entity\User
|
class: Tvdt\Entity\User
|
||||||
property: email
|
property: email
|
||||||
# used to reload user from session & other features (e.g. switch_user)
|
|
||||||
firewalls:
|
firewalls:
|
||||||
dev:
|
dev:
|
||||||
# Ensure dev tools and static assets are always allowed
|
# Ensure dev tools and static assets are always allowed
|
||||||
@@ -26,15 +24,7 @@ security:
|
|||||||
default_target_path: tvdt_backoffice_index
|
default_target_path: tvdt_backoffice_index
|
||||||
logout:
|
logout:
|
||||||
path: tvdt_login_logout
|
path: tvdt_login_logout
|
||||||
# where to redirect after logout
|
|
||||||
# target: tvdt_any_route
|
|
||||||
|
|
||||||
# activate different ways to authenticate
|
|
||||||
# https://symfony.com/doc/current/security.html#the-firewall
|
|
||||||
|
|
||||||
# https://symfony.com/doc/current/security/impersonating_user.html
|
|
||||||
# switch_user: true
|
|
||||||
|
|
||||||
access_control:
|
access_control:
|
||||||
- { path: ^/admin, roles: ROLE_ADMIN }
|
- { path: ^/admin, roles: ROLE_ADMIN }
|
||||||
- { path: ^/backoffice, roles: ROLE_USER }
|
- { path: ^/backoffice, roles: ROLE_USER }
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ when@prod:
|
|||||||
ignore_exceptions:
|
ignore_exceptions:
|
||||||
- 'Symfony\Component\ErrorHandler\Error\FatalError'
|
- 'Symfony\Component\ErrorHandler\Error\FatalError'
|
||||||
- 'Symfony\Component\Debug\Exception\FatalErrorException'
|
- 'Symfony\Component\Debug\Exception\FatalErrorException'
|
||||||
|
- 'Symfony\Component\HttpKernel\Exception\NotFoundHttpException'
|
||||||
#
|
#
|
||||||
# # If you are using Monolog, you also need this additional configuration to log the errors correctly:
|
# # If you are using Monolog, you also need this additional configuration to log the errors correctly:
|
||||||
# # https://docs.sentry.io/platforms/php/guides/symfony/integrations/monolog/
|
# # https://docs.sentry.io/platforms/php/guides/symfony/integrations/monolog/
|
||||||
|
|||||||
+6
-6
@@ -22,23 +22,23 @@ return [
|
|||||||
'path' => './assets/backoffice.js',
|
'path' => './assets/backoffice.js',
|
||||||
'entrypoint' => true,
|
'entrypoint' => true,
|
||||||
],
|
],
|
||||||
|
'@symfony/stimulus-bundle' => [
|
||||||
|
'path' => './vendor/symfony/stimulus-bundle/assets/dist/loader.js',
|
||||||
|
],
|
||||||
'bootstrap' => [
|
'bootstrap' => [
|
||||||
'version' => '5.3.6',
|
'version' => '5.3.8',
|
||||||
],
|
],
|
||||||
'@popperjs/core' => [
|
'@popperjs/core' => [
|
||||||
'version' => '2.11.8',
|
'version' => '2.11.8',
|
||||||
],
|
],
|
||||||
'bootstrap/dist/css/bootstrap.min.css' => [
|
'bootstrap/dist/css/bootstrap.min.css' => [
|
||||||
'version' => '5.3.6',
|
'version' => '5.3.8',
|
||||||
'type' => 'css',
|
'type' => 'css',
|
||||||
],
|
],
|
||||||
'@hotwired/stimulus' => [
|
'@hotwired/stimulus' => [
|
||||||
'version' => '3.2.2',
|
'version' => '3.2.2',
|
||||||
],
|
],
|
||||||
'@symfony/stimulus-bundle' => [
|
|
||||||
'path' => './vendor/symfony/stimulus-bundle/assets/dist/loader.js',
|
|
||||||
],
|
|
||||||
'@hotwired/turbo' => [
|
'@hotwired/turbo' => [
|
||||||
'version' => '7.3.0',
|
'version' => '8.0.23',
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ use Symfony\Component\Security\Http\Attribute\IsGranted;
|
|||||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
use Tvdt\Controller\AbstractController;
|
use Tvdt\Controller\AbstractController;
|
||||||
use Tvdt\Entity\Candidate;
|
use Tvdt\Entity\Candidate;
|
||||||
|
use Tvdt\Entity\Question;
|
||||||
use Tvdt\Entity\Quiz;
|
use Tvdt\Entity\Quiz;
|
||||||
use Tvdt\Entity\Season;
|
use Tvdt\Entity\Season;
|
||||||
use Tvdt\Exception\ErrorClearingQuizException;
|
use Tvdt\Exception\ErrorClearingQuizException;
|
||||||
@@ -40,11 +41,122 @@ class QuizController extends AbstractController
|
|||||||
requirements: ['seasonCode' => self::SEASON_CODE_REGEX, 'quiz' => Requirement::UUID],
|
requirements: ['seasonCode' => self::SEASON_CODE_REGEX, 'quiz' => Requirement::UUID],
|
||||||
)]
|
)]
|
||||||
public function index(Season $season, Quiz $quiz): Response
|
public function index(Season $season, Quiz $quiz): Response
|
||||||
|
{
|
||||||
|
return $this->redirectToRoute('tvdt_backoffice_quiz_overview', ['seasonCode' => $season->seasonCode, 'quiz' => $quiz->id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[IsGranted(SeasonVoter::EDIT, subject: 'season')]
|
||||||
|
#[Route(
|
||||||
|
'/backoffice/season/{seasonCode:season}/quiz/{quiz}/overview',
|
||||||
|
name: 'tvdt_backoffice_quiz_overview',
|
||||||
|
requirements: ['seasonCode' => self::SEASON_CODE_REGEX, 'quiz' => Requirement::UUID],
|
||||||
|
)]
|
||||||
|
public function overview(Season $season, Quiz $quiz): Response
|
||||||
|
{
|
||||||
|
$fetchedQuiz = $this->quizRepository->fetchWithQuestions($quiz->id);
|
||||||
|
|
||||||
|
return $this->render('backoffice/quiz.html.twig', [
|
||||||
|
'season' => $season,
|
||||||
|
'quiz' => $fetchedQuiz,
|
||||||
|
'activeTab' => 'overview',
|
||||||
|
'template' => 'backoffice/quiz/tab_overview.html.twig',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[IsGranted(SeasonVoter::EDIT, subject: 'season')]
|
||||||
|
#[Route(
|
||||||
|
'/backoffice/season/{seasonCode:season}/quiz/{quiz}/result',
|
||||||
|
name: 'tvdt_backoffice_quiz_result',
|
||||||
|
requirements: ['seasonCode' => self::SEASON_CODE_REGEX, 'quiz' => Requirement::UUID],
|
||||||
|
)]
|
||||||
|
public function result(Season $season, Quiz $quiz): Response
|
||||||
|
{
|
||||||
|
$fetchedQuiz = $this->quizRepository->fetchWithQuestions($quiz->id);
|
||||||
|
|
||||||
|
return $this->render('backoffice/quiz.html.twig', [
|
||||||
|
'season' => $season,
|
||||||
|
'quiz' => $fetchedQuiz,
|
||||||
|
'result' => $this->quizRepository->getScores($quiz),
|
||||||
|
'activeTab' => 'result',
|
||||||
|
'template' => 'backoffice/quiz/tab_result.html.twig',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[IsGranted(SeasonVoter::EDIT, subject: 'season')]
|
||||||
|
#[Route(
|
||||||
|
'/backoffice/season/{seasonCode:season}/quiz/{quiz}/candidates',
|
||||||
|
name: 'tvdt_backoffice_quiz_candidates',
|
||||||
|
requirements: ['seasonCode' => self::SEASON_CODE_REGEX, 'quiz' => Requirement::UUID],
|
||||||
|
)]
|
||||||
|
public function candidates(Season $season, Quiz $quiz): Response
|
||||||
|
{
|
||||||
|
$fetchedQuiz = $this->quizRepository->fetchWithQuestions($quiz->id);
|
||||||
|
assert($fetchedQuiz->questions->count() > 0);
|
||||||
|
$firstQuestion = $fetchedQuiz->questions->first();
|
||||||
|
|
||||||
|
return $this->redirectToRoute('tvdt_backoffice_quiz_candidates_question', [
|
||||||
|
'seasonCode' => $season->seasonCode,
|
||||||
|
'quiz' => $quiz->id,
|
||||||
|
'question' => $firstQuestion->id,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[IsGranted(SeasonVoter::EDIT, subject: 'season')]
|
||||||
|
#[Route(
|
||||||
|
'/backoffice/season/{seasonCode:season}/quiz/{quiz}/candidates/{question}',
|
||||||
|
name: 'tvdt_backoffice_quiz_candidates_question',
|
||||||
|
requirements: ['seasonCode' => self::SEASON_CODE_REGEX, 'quiz' => Requirement::UUID],
|
||||||
|
methods: ['GET']
|
||||||
|
)]
|
||||||
|
public function candidates_question(Season $season, Quiz $quiz, Question $question): Response
|
||||||
{
|
{
|
||||||
return $this->render('backoffice/quiz.html.twig', [
|
return $this->render('backoffice/quiz.html.twig', [
|
||||||
'season' => $season,
|
'season' => $season,
|
||||||
'quiz' => $quiz,
|
'quiz' => $quiz,
|
||||||
'result' => $this->quizRepository->getScores($quiz),
|
'question' => $question,
|
||||||
|
'candidates' => $season->candidates,
|
||||||
|
'activeTab' => 'candidates',
|
||||||
|
'template' => 'backoffice/quiz/tab_candidates.html.twig',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[IsGranted(SeasonVoter::EDIT, subject: 'season')]
|
||||||
|
#[Route(
|
||||||
|
'/backoffice/season/{seasonCode:season}/quiz/{quiz}/candidates/{question}',
|
||||||
|
name: 'tvdt_backoffice_quiz_candidates_question_save',
|
||||||
|
requirements: ['seasonCode' => self::SEASON_CODE_REGEX, 'quiz' => Requirement::UUID],
|
||||||
|
methods: ['POST']
|
||||||
|
)]
|
||||||
|
public function saveCandidateAnswers(Season $season, Quiz $quiz, Question $question, Request $request, EntityManagerInterface $em): RedirectResponse
|
||||||
|
{
|
||||||
|
$candidateAnswers = $request->request->all('candidate_answer');
|
||||||
|
|
||||||
|
// Clear existing candidate-answer associations for this question
|
||||||
|
foreach ($question->answers as $answer) {
|
||||||
|
$answer->candidates->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add new associations
|
||||||
|
foreach ($candidateAnswers as $candidateId => $answerIds) {
|
||||||
|
$candidate = $em->getRepository(Candidate::class)->find($candidateId);
|
||||||
|
|
||||||
|
foreach ((array) $answerIds as $answerId) {
|
||||||
|
$answer = $em->getRepository(\Tvdt\Entity\Answer::class)->find($answerId);
|
||||||
|
if ($answer && $candidate) {
|
||||||
|
$answer->addCandidate($candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$em->flush();
|
||||||
|
|
||||||
|
$this->addFlash('success', $this->translator->trans('Candidate answers saved'));
|
||||||
|
|
||||||
|
return $this->redirectToRoute('tvdt_backoffice_quiz_candidates_question', [
|
||||||
|
'seasonCode' => $season->seasonCode,
|
||||||
|
'quiz' => $quiz->id,
|
||||||
|
'question' => $question->id,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,4 +57,10 @@ class Answer
|
|||||||
{
|
{
|
||||||
$this->candidates->removeElement($candidate);
|
$this->candidates->removeElement($candidate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function __toString(): string
|
||||||
|
{
|
||||||
|
return $this->text;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,6 +70,71 @@ class Question
|
|||||||
return 'This question has multiple correct answers';
|
return 'This question has multiple correct answers';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if any answer in the entire quiz has candidate relations
|
||||||
|
$hasCandidateRelations = false;
|
||||||
|
foreach ($this->quiz->questions as $quizQuestion) {
|
||||||
|
foreach ($quizQuestion->answers as $answer) {
|
||||||
|
if ($answer->candidates->count() > 0) {
|
||||||
|
$hasCandidateRelations = true;
|
||||||
|
break 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only validate candidate-answer relations if at least one exists in the quiz
|
||||||
|
if ($hasCandidateRelations) {
|
||||||
|
$seasonCandidates = $this->quiz->season->candidates;
|
||||||
|
$candidateCounts = [];
|
||||||
|
|
||||||
|
// Count how many times each candidate appears in answers
|
||||||
|
foreach ($this->answers as $answer) {
|
||||||
|
foreach ($answer->candidates as $candidate) {
|
||||||
|
$candidateId = $candidate->id->toString();
|
||||||
|
if (!isset($candidateCounts[$candidateId])) {
|
||||||
|
$candidateCounts[$candidateId] = ['name' => $candidate->name, 'count' => 0];
|
||||||
|
}
|
||||||
|
++$candidateCounts[$candidateId]['count'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for missing and duplicate candidates
|
||||||
|
$missing = [];
|
||||||
|
$duplicates = [];
|
||||||
|
|
||||||
|
foreach ($seasonCandidates as $candidate) {
|
||||||
|
$candidateId = $candidate->id->toString();
|
||||||
|
$count = $candidateCounts[$candidateId]['count'] ?? 0;
|
||||||
|
|
||||||
|
if (0 === $count) {
|
||||||
|
$missing[] = $candidate->name;
|
||||||
|
} elseif ($count > 1) {
|
||||||
|
$duplicates[] = $candidate->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($missing) || !empty($duplicates)) {
|
||||||
|
$errors = [];
|
||||||
|
if (!empty($missing)) {
|
||||||
|
// If all candidates are missing, show a special message
|
||||||
|
if (\count($missing) === $seasonCandidates->count()) {
|
||||||
|
$errors[] = 'No candidates assigned to this question';
|
||||||
|
} else {
|
||||||
|
$errors[] = 'Missing candidates: '.implode(', ', $missing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!empty($duplicates)) {
|
||||||
|
$errors[] = 'Duplicate candidates: '.implode(', ', $duplicates);
|
||||||
|
}
|
||||||
|
|
||||||
|
return implode('. ', $errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function __toString(): string
|
||||||
|
{
|
||||||
|
return $this->question;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use Doctrine\Persistence\ManagerRegistry;
|
|||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Safe\DateTimeImmutable;
|
use Safe\DateTimeImmutable;
|
||||||
use Safe\Exceptions\DatetimeException;
|
use Safe\Exceptions\DatetimeException;
|
||||||
|
use Symfony\Component\Uid\Uuid;
|
||||||
use Tvdt\Dto\Result;
|
use Tvdt\Dto\Result;
|
||||||
use Tvdt\Entity\Quiz;
|
use Tvdt\Entity\Quiz;
|
||||||
use Tvdt\Exception\ErrorClearingQuizException;
|
use Tvdt\Exception\ErrorClearingQuizException;
|
||||||
@@ -105,4 +106,14 @@ class QuizRepository extends ServiceEntityRepository
|
|||||||
score: $row['score'],
|
score: $row['score'],
|
||||||
), $result);
|
), $result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function fetchWithQuestions(Uuid $id): Quiz
|
||||||
|
{
|
||||||
|
return $this->getEntityManager()->createQuery(<<<dql
|
||||||
|
select q, qz, a from Tvdt\Entity\Quiz q
|
||||||
|
join q.questions qz
|
||||||
|
join qz.answers a
|
||||||
|
where q.id = :id
|
||||||
|
dql)->setParameter('id', $id)->getSingleResult();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,182 +1,25 @@
|
|||||||
{% extends 'backoffice/base.html.twig' %}
|
{% extends 'backoffice/base.html.twig' %}
|
||||||
|
|
||||||
{% block title %}{{ parent() }}{{ quiz.season.name }}{% endblock %}
|
{% block title %}{{ parent() }}{{ season.name }}{% endblock %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<h2 class="py-2">{{ 'Quiz'|trans }}: {{ quiz.season.name }} - {{ quiz.name }}</h2>
|
{% set tabs = [
|
||||||
<div class="py-2 btn-group" data-controller="bo--quiz">
|
{id: 'overview', label: 'Overview'|trans, route: 'tvdt_backoffice_quiz_overview'},
|
||||||
<a class="btn btn-primary {% if quiz is same as(season.activeQuiz) %}disabled{% endif %}"
|
{id: 'result', label: 'Results & Elimination'|trans, route: 'tvdt_backoffice_quiz_result'},
|
||||||
href="{{ path('tvdt_backoffice_enable', {seasonCode: season.seasonCode, quiz: quiz.id}) }}">{{ 'Make active'|trans }}</a>
|
{id: 'candidates', label: 'Candidates'|trans, route: 'tvdt_backoffice_quiz_candidates'},
|
||||||
{% if quiz is same as (season.activeQuiz) %}
|
] %}
|
||||||
<a class="btn btn-secondary"
|
|
||||||
href="{{ path('tvdt_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">
|
<h2 class="py-2">{{ 'Quiz'|trans }}: {{ season.name }} - {{ quiz.name }}</h2>
|
||||||
<h4 class="py-2">{{ 'Questions'|trans }}</h4>
|
<ul class="nav nav-tabs pt-2">
|
||||||
<div class="accordion">
|
{% for tab in tabs %}
|
||||||
{%~ for question in quiz.questions ~%}
|
<li class="nav-item">
|
||||||
<div class="accordion-item">
|
<a class="nav-link{{ activeTab == tab.id ? ' active' }}" href="{{ path(tab.route, {seasonCode: season.seasonCode, quiz: quiz.id}) }}">
|
||||||
<h2 class="accordion-header">
|
{{ tab.label }}
|
||||||
<button class="accordion-button collapsed"
|
</a>
|
||||||
type="button"
|
</li>
|
||||||
data-bs-toggle="collapse"
|
{% endfor %}
|
||||||
data-bs-target="#question-{{ loop.index0 }}"
|
</ul>
|
||||||
aria-controls="question-{{ loop.index0 }}">
|
<div class="pt-3">
|
||||||
{% set questionErrors = question.getErrors %}
|
{{ include(template) }}
|
||||||
{%~ if questionErrors -%}
|
|
||||||
<span data-bs-toggle="tooltip"
|
|
||||||
title="{{ questionErrors }}"
|
|
||||||
class="badge text-bg-danger rounded-pill me-2">!</span>
|
|
||||||
{% endif %}
|
|
||||||
{{~ loop.index -}}. {{ question.question -}}
|
|
||||||
</button>
|
|
||||||
</h2>
|
|
||||||
<div id="question-{{ loop.index0 }}"
|
|
||||||
class="accordion-collapse collapse">
|
|
||||||
<div class="accordion-body">
|
|
||||||
<ul>
|
|
||||||
{%~ for answer in question.answers %}
|
|
||||||
<li{% if answer.isRightAnswer %} class="text-decoration-underline"{% endif %}>{{ answer.text -}}</li>
|
|
||||||
{%~ else %}
|
|
||||||
{{ 'There are no answers for this question'|trans -}}
|
|
||||||
{%~ endfor %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
{{ 'EMPTY'|trans }}
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="scores">
|
|
||||||
<h4 class="py-2">{{ 'Score'|trans }}</h4>
|
|
||||||
<div class="btn-toolbar" role="toolbar">
|
|
||||||
<div class="btn-group btn-group-lg me-2">
|
|
||||||
{# <a class="btn btn-primary">{{ 'Start Elimination'|trans }}</a> #}
|
|
||||||
<a href="{{ path('tvdt_prepare_elimination', {seasonCode: season.seasonCode, quiz: quiz.id}) }}"
|
|
||||||
class="btn btn-secondary">{{ 'Prepare Custom Elimination'|trans }}</a>
|
|
||||||
{%~ if not quiz.eliminations.empty %}
|
|
||||||
<button class="btn btn-secondary dropdown-toggle"
|
|
||||||
data-bs-toggle="dropdown">{{ 'Load Prepared Elimination'|trans }}</button>
|
|
||||||
<ul class="dropdown-menu">
|
|
||||||
{%~ for elimination in quiz.eliminations %}
|
|
||||||
<li><a class="dropdown-item"
|
|
||||||
href="{{ path('tvdt_prepare_elimination_view', {elimination: elimination.id}) }}">{{ elimination.created|format_datetime() }}</a>
|
|
||||||
</li>
|
|
||||||
{%~ endfor %}
|
|
||||||
</ul>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p>{{ 'Number of dropouts:'|trans }} {{ quiz.dropouts }} </p>
|
|
||||||
<table class="table table-hover">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th scope="col">{{ 'Candidate'|trans }}</th>
|
|
||||||
<th style="width: 15%" scope="col">{{ 'Correct Answers'|trans }}</th>
|
|
||||||
<th style="width: 20%" scope="col">{{ 'Corrections'|trans }}</th>
|
|
||||||
<th style="width: 20%" scope="col">{{ 'Penalty'|trans }}</th>
|
|
||||||
<th style="width: 10%" scope="col">{{ 'Score'|trans }}</th>
|
|
||||||
<th style="width: 20%" scope="col">{{ 'Time'|trans }}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{%~ for candidate in result ~%}
|
|
||||||
<tr class="table-{% if loop.revindex > quiz.dropouts %}success{% else %}danger{% endif %}">
|
|
||||||
<td>{{ candidate.name }}</td>
|
|
||||||
<td>{{ candidate.correct|default('0') }}</td>
|
|
||||||
<td>
|
|
||||||
<form method="post"
|
|
||||||
action="{{ path('tvdt_backoffice_modify_correction', {quiz: quiz.id, candidate: candidate.id}) }}">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-8">
|
|
||||||
<input class="form-control form-control-sm" type="number"
|
|
||||||
value="{{ candidate.corrections }}" step="0.5"
|
|
||||||
name="corrections">
|
|
||||||
</div>
|
|
||||||
<div class="col-2">
|
|
||||||
<button class="btn btn-sm btn-primary" type="submit">{{ 'Save'|trans }}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<form method="post"
|
|
||||||
action="{{ path('tvdt_backoffice_modify_penalty', {quiz: quiz.id, candidate: candidate.id}) }}">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-8">
|
|
||||||
<input class="form-control form-control-sm" type="number"
|
|
||||||
value="{{ candidate.penaltySeconds }}" step="1"
|
|
||||||
name="penalty">
|
|
||||||
</div>
|
|
||||||
<div class="col-2">
|
|
||||||
<button class="btn btn-sm btn-primary" type="submit">{{ 'Save'|trans }}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</td>
|
|
||||||
<td>{{ candidate.score|default('x') }}</td>
|
|
||||||
<td>{{ candidate.time.format('%i:%S') }}</td>
|
|
||||||
</tr>
|
|
||||||
{% else %}
|
|
||||||
<tr>
|
|
||||||
<td colspan="5">{{ 'No results'|trans }}</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{# 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'|trans }}</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.'|trans }}
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{ 'No'|trans }}</button>
|
|
||||||
<a href="{{ path('tvdt_backoffice_quiz_clear', {quiz: quiz.id}) }}"
|
|
||||||
class="btn btn-danger">{{ 'Yes'|trans }}</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'|trans }}</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?'|trans }}
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{ 'No'|trans }}</button>
|
|
||||||
<a href="{{ path('tvdt_backoffice_quiz_delete', {quiz: quiz.id}) }}"
|
|
||||||
class="btn btn-danger">{{ 'Yes'|trans }}</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||||
|
<div>
|
||||||
|
{% set questions = quiz.questions %}
|
||||||
|
{% set currentIndex = null %}
|
||||||
|
{% for index, q in questions %}
|
||||||
|
{% if q.id.toString == question.id.toString %}
|
||||||
|
{% set currentIndex = index %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% if currentIndex > 0 %}
|
||||||
|
{% set prevQuestion = questions[currentIndex - 1] %}
|
||||||
|
<a href="{{ path('tvdt_backoffice_quiz_candidates_question', {seasonCode: season.seasonCode, quiz: quiz.id, question: prevQuestion.id}) }}"
|
||||||
|
class="btn btn-secondary">
|
||||||
|
{{ 'Previous'|trans }}
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 class="mb-0">{{ currentIndex + 1 }}. {{ question }}</h3>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{% if currentIndex is not null and currentIndex < (questions|length - 1) %}
|
||||||
|
{% set nextQuestion = questions[currentIndex + 1] %}
|
||||||
|
<a href="{{ path('tvdt_backoffice_quiz_candidates_question', {seasonCode: season.seasonCode, quiz: quiz.id, question: nextQuestion.id}) }}"
|
||||||
|
class="btn btn-secondary">
|
||||||
|
{{ 'Next'|trans }}
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form method="post">
|
||||||
|
<table class="table table-hover table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">{{ 'Candidate'|trans }}</th>
|
||||||
|
{% for answer in question.answers %}
|
||||||
|
<th>{{ answer }}</th>
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for candidate in candidates %}
|
||||||
|
<tr>
|
||||||
|
<th scope="row">{{ candidate.name }}</th>
|
||||||
|
{% for answer in question.answers %}
|
||||||
|
<td>
|
||||||
|
<input type="checkbox"
|
||||||
|
id="candidate_{{ candidate.id }}_answer_{{ answer.id }}"
|
||||||
|
name="candidate_answer[{{ candidate.id }}][]"
|
||||||
|
value="{{ answer.id }}"
|
||||||
|
class="form-check-input"
|
||||||
|
{{ answer.candidates.contains(candidate) ? 'checked' : '' }}>
|
||||||
|
<label for="candidate_{{ candidate.id }}_answer_{{ answer.id }}" class="visually-hidden">
|
||||||
|
{{ candidate.name }} - {{ answer }}
|
||||||
|
</label>
|
||||||
|
</td>
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<button type="submit" class="btn btn-primary">{{ 'Save'|trans }}</button>
|
||||||
|
</form>
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
<h4 class="py-2">Quick actions</h4>
|
||||||
|
<div class="py-2 btn-group" data-controller="bo--quiz">
|
||||||
|
|
||||||
|
{% if quiz is same as (season.activeQuiz) %}
|
||||||
|
<a class="btn btn-secondary"
|
||||||
|
href="{{ path('tvdt_backoffice_enable', {seasonCode: season.seasonCode, quiz: 'null'}) }}">{{ 'Deactivate Quiz'|trans }}</a>
|
||||||
|
{% else %}
|
||||||
|
<a class="btn btn-primary"
|
||||||
|
href="{{ path('tvdt_backoffice_enable', {seasonCode: season.seasonCode, quiz: quiz.id}) }}">{{ 'Make active'|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">
|
||||||
|
<h4 class="py-2">{{ 'Questions'|trans }}</h4>
|
||||||
|
<div class="accordion">
|
||||||
|
{%~ for question in quiz.questions ~%}
|
||||||
|
<div class="accordion-item">
|
||||||
|
<h2 class="accordion-header">
|
||||||
|
<button class="accordion-button collapsed"
|
||||||
|
type="button"
|
||||||
|
data-bs-toggle="collapse"
|
||||||
|
data-bs-target="#question-{{ loop.index0 }}"
|
||||||
|
aria-controls="question-{{ loop.index0 }}">
|
||||||
|
{% set questionErrors = question.getErrors %}
|
||||||
|
{%~ if questionErrors -%}
|
||||||
|
<span data-bs-toggle="tooltip"
|
||||||
|
title="{{ questionErrors }}"
|
||||||
|
class="badge text-bg-danger rounded-pill me-2">!</span>
|
||||||
|
{% endif %}
|
||||||
|
{{~ loop.index -}}. {{ question.question -}}
|
||||||
|
</button>
|
||||||
|
</h2>
|
||||||
|
<div id="question-{{ loop.index0 }}"
|
||||||
|
class="accordion-collapse collapse">
|
||||||
|
<div class="accordion-body">
|
||||||
|
<ul>
|
||||||
|
{%~ for answer in question.answers %}
|
||||||
|
<li{% if answer.isRightAnswer %} class="text-decoration-underline"{% endif %}>
|
||||||
|
{{ answer.text }}
|
||||||
|
{% if answer.candidates|length > 0 %}
|
||||||
|
<small class="text-muted">
|
||||||
|
({{ answer.candidates|map(c => c.name)|join(', ') }})
|
||||||
|
</small>
|
||||||
|
{% endif %}
|
||||||
|
</li>
|
||||||
|
{%~ else %}
|
||||||
|
{{ 'There are no answers for this question'|trans -}}
|
||||||
|
{%~ endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
{{ 'EMPTY'|trans }}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{# 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'|trans }}</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.'|trans }}
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{ 'No'|trans }}</button>
|
||||||
|
<a href="{{ path('tvdt_backoffice_quiz_clear', {quiz: quiz.id}) }}"
|
||||||
|
class="btn btn-danger">{{ 'Yes'|trans }}</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'|trans }}</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?'|trans }}
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{ 'No'|trans }}</button>
|
||||||
|
<a href="{{ path('tvdt_backoffice_quiz_delete', {quiz: quiz.id}) }}"
|
||||||
|
class="btn btn-danger">{{ 'Yes'|trans }}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
<div class="scores">
|
||||||
|
<h4 class="py-2">{{ 'Score'|trans }}</h4>
|
||||||
|
<div class="btn-toolbar" role="toolbar">
|
||||||
|
<div class="btn-group btn-group-lg me-2">
|
||||||
|
{# <a class="btn btn-primary">{{ 'Start Elimination'|trans }}</a> #}
|
||||||
|
<a href="{{ path('tvdt_prepare_elimination', {seasonCode: season.seasonCode, quiz: quiz.id}) }}"
|
||||||
|
class="btn btn-secondary">{{ 'Prepare Custom Elimination'|trans }}</a>
|
||||||
|
{%~ if not quiz.eliminations.empty %}
|
||||||
|
<button class="btn btn-secondary dropdown-toggle"
|
||||||
|
data-bs-toggle="dropdown">{{ 'Load Prepared Elimination'|trans }}</button>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
{%~ for elimination in quiz.eliminations %}
|
||||||
|
<li><a class="dropdown-item"
|
||||||
|
href="{{ path('tvdt_prepare_elimination_view', {elimination: elimination.id}) }}">{{ elimination.created|format_datetime() }}</a>
|
||||||
|
</li>
|
||||||
|
{%~ endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p>{{ 'Number of dropouts:'|trans }} {{ quiz.dropouts }} </p>
|
||||||
|
<table class="table table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">{{ 'Candidate'|trans }}</th>
|
||||||
|
<th style="width: 15%" scope="col">{{ 'Correct Answers'|trans }}</th>
|
||||||
|
<th style="width: 20%" scope="col">{{ 'Corrections'|trans }}</th>
|
||||||
|
<th style="width: 20%" scope="col">{{ 'Penalty'|trans }}</th>
|
||||||
|
<th style="width: 10%" scope="col">{{ 'Score'|trans }}</th>
|
||||||
|
<th style="width: 20%" scope="col">{{ 'Time'|trans }}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{%~ for candidate in result ~%}
|
||||||
|
<tr class="table-{% if loop.revindex > quiz.dropouts %}success{% else %}danger{% endif %}">
|
||||||
|
<td>{{ candidate.name }}</td>
|
||||||
|
<td>{{ candidate.correct|default('0') }}</td>
|
||||||
|
<td>
|
||||||
|
<form method="post"
|
||||||
|
action="{{ path('tvdt_backoffice_modify_correction', {quiz: quiz.id, candidate: candidate.id}) }}">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-8">
|
||||||
|
<input class="form-control form-control-sm" type="number"
|
||||||
|
value="{{ candidate.corrections }}" step="0.5"
|
||||||
|
name="corrections">
|
||||||
|
</div>
|
||||||
|
<div class="col-2">
|
||||||
|
<button class="btn btn-sm btn-primary" type="submit">{{ 'Save'|trans }}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<form method="post"
|
||||||
|
action="{{ path('tvdt_backoffice_modify_penalty', {quiz: quiz.id, candidate: candidate.id}) }}">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-8">
|
||||||
|
<input class="form-control form-control-sm" type="number"
|
||||||
|
value="{{ candidate.penaltySeconds }}" step="1"
|
||||||
|
name="penalty">
|
||||||
|
</div>
|
||||||
|
<div class="col-2">
|
||||||
|
<button class="btn btn-sm btn-primary" type="submit">{{ 'Save'|trans }}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
<td>{{ candidate.score|default('x') }}</td>
|
||||||
|
<td>{{ candidate.time.format('%i:%S') }}</td>
|
||||||
|
</tr>
|
||||||
|
{% else %}
|
||||||
|
<tr>
|
||||||
|
<td colspan="5">{{ 'No results'|trans }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
@@ -57,8 +57,8 @@
|
|||||||
<source>Candidates</source>
|
<source>Candidates</source>
|
||||||
<target>Kandidaten</target>
|
<target>Kandidaten</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="FNY513f" resname="Clear quiz...">
|
<trans-unit id="J1c2y63" resname="Clear Quiz...">
|
||||||
<source>Clear quiz...</source>
|
<source>Clear Quiz...</source>
|
||||||
<target>Test leegmaken...</target>
|
<target>Test leegmaken...</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sFpB4C2" resname="Correct Answers">
|
<trans-unit id="sFpB4C2" resname="Correct Answers">
|
||||||
@@ -149,14 +149,14 @@
|
|||||||
<source>Name</source>
|
<source>Name</source>
|
||||||
<target>Naam</target>
|
<target>Naam</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="wd1MvZW" resname="No">
|
|
||||||
<source>No</source>
|
|
||||||
<target>Nee</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="gefhnBC" resname="Next">
|
<trans-unit id="gefhnBC" resname="Next">
|
||||||
<source>Next</source>
|
<source>Next</source>
|
||||||
<target>Volgende</target>
|
<target>Volgende</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="wd1MvZW" resname="No">
|
||||||
|
<source>No</source>
|
||||||
|
<target>Nee</target>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="nOHriCl" resname="No active quiz">
|
<trans-unit id="nOHriCl" resname="No active quiz">
|
||||||
<source>No active quiz</source>
|
<source>No active quiz</source>
|
||||||
<target>Geen actieve test</target>
|
<target>Geen actieve test</target>
|
||||||
@@ -169,6 +169,10 @@
|
|||||||
<source>Number of dropouts:</source>
|
<source>Number of dropouts:</source>
|
||||||
<target>Aantal afvallers:</target>
|
<target>Aantal afvallers:</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="HmgPmMV" resname="Overview">
|
||||||
|
<source>Overview</source>
|
||||||
|
<target>Overzicht</target>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="PywqOf4" resname="Owner(s)">
|
<trans-unit id="PywqOf4" resname="Owner(s)">
|
||||||
<source>Owner(s)</source>
|
<source>Owner(s)</source>
|
||||||
<target>Eigena(a)r(en)</target>
|
<target>Eigena(a)r(en)</target>
|
||||||
@@ -177,6 +181,10 @@
|
|||||||
<source>Password</source>
|
<source>Password</source>
|
||||||
<target>Wachtwoord</target>
|
<target>Wachtwoord</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="1ne1Zlc" resname="Penalty">
|
||||||
|
<source>Penalty</source>
|
||||||
|
<target>Straf</target>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="VbgD9L8" resname="Please Confirm">
|
<trans-unit id="VbgD9L8" resname="Please Confirm">
|
||||||
<source>Please Confirm</source>
|
<source>Please Confirm</source>
|
||||||
<target>Bevestig Alsjeblieft</target>
|
<target>Bevestig Alsjeblieft</target>
|
||||||
@@ -249,6 +257,10 @@
|
|||||||
<source>Repeat Password</source>
|
<source>Repeat Password</source>
|
||||||
<target>Herhaal wachtwoord</target>
|
<target>Herhaal wachtwoord</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="7UvBPrb" resname="Results & Elimination">
|
||||||
|
<source>Results & Elimination</source>
|
||||||
|
<target><![CDATA[Resultaat & Eliminatie]]></target>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="z9OKodR" resname="Save">
|
<trans-unit id="z9OKodR" resname="Save">
|
||||||
<source>Save</source>
|
<source>Save</source>
|
||||||
<target>Opslaan</target>
|
<target>Opslaan</target>
|
||||||
|
|||||||
Reference in New Issue
Block a user