svelte-migrate: updated files
This commit is contained in:
parent
4d6149760d
commit
ff5a2eef65
36 changed files with 1252 additions and 1384 deletions
9
ui/src/routes/surveys/[sid]/+layout.js
Normal file
9
ui/src/routes/surveys/[sid]/+layout.js
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import { getSurvey } from '$lib/surveys';
|
||||
|
||||
export async function load({ params }) {
|
||||
const survey = getSurvey(params.sid);
|
||||
|
||||
return {
|
||||
survey,
|
||||
};
|
||||
}
|
||||
|
|
@ -1,27 +1,8 @@
|
|||
<script context="module">
|
||||
import { getSurvey } from '$lib/surveys';
|
||||
|
||||
export async function load({ params, stuff }) {
|
||||
const survey = getSurvey(params.sid);
|
||||
|
||||
return {
|
||||
props: {
|
||||
survey,
|
||||
},
|
||||
stuff: {
|
||||
...stuff,
|
||||
survey,
|
||||
}
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
|
||||
export let survey;
|
||||
export let data;
|
||||
</script>
|
||||
|
||||
{#await survey}
|
||||
{#await data.survey}
|
||||
<div class="text-center">
|
||||
<div class="spinner-border text-primary mx-3" role="status"></div>
|
||||
<span>Chargement du questionnaire …</span>
|
||||
|
|
|
|||
5
ui/src/routes/surveys/[sid]/+page.js
Normal file
5
ui/src/routes/surveys/[sid]/+page.js
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
export async function load({ parent }) {
|
||||
const stuff = await parent();
|
||||
|
||||
return stuff;
|
||||
}
|
||||
|
|
@ -1,15 +1,3 @@
|
|||
<script context="module">
|
||||
import { getSurvey } from '$lib/surveys';
|
||||
|
||||
export async function load({ params, stuff }) {
|
||||
return {
|
||||
props: {
|
||||
surveyP: stuff.survey,
|
||||
},
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
|
|
@ -19,62 +7,58 @@
|
|||
import SurveyQuestions from '$lib/components/SurveyQuestions.svelte';
|
||||
import { getQuestions } from '$lib/questions';
|
||||
|
||||
export let surveyP;
|
||||
export let data;
|
||||
let survey = null;
|
||||
|
||||
$: {
|
||||
if (surveyP) {
|
||||
surveyP.then((survey) => {
|
||||
if (survey.direct && !$user.is_admin) {
|
||||
goto(`surveys/${survey.id}/live`);
|
||||
}
|
||||
})
|
||||
survey = data.survey;
|
||||
if (survey.direct && !$user.is_admin) {
|
||||
goto(`surveys/${survey.id}/live`);
|
||||
}
|
||||
}
|
||||
|
||||
let edit = false;
|
||||
</script>
|
||||
|
||||
{#await surveyP then survey}
|
||||
{#if $user && $user.is_admin}
|
||||
<button class="btn btn-primary ms-1 float-end" on:click={() => { edit = !edit; } } title="Éditer"><i class="bi bi-pencil"></i></button>
|
||||
<a href="surveys/{survey.id}/responses" class="btn btn-success ms-1 float-end" title="Voir les réponses"><i class="bi bi-files"></i></a>
|
||||
{#if survey.direct}
|
||||
<a href="surveys/{survey.id}/live" class="btn btn-danger ms-1 float-end" title="Aller au direct"><i class="bi bi-film"></i></a>
|
||||
{/if}
|
||||
{#if $user && $user.is_admin}
|
||||
<button class="btn btn-primary ms-1 float-end" on:click={() => { edit = !edit; } } title="Éditer"><i class="bi bi-pencil"></i></button>
|
||||
<a href="surveys/{survey.id}/responses" class="btn btn-success ms-1 float-end" title="Voir les réponses"><i class="bi bi-files"></i></a>
|
||||
{#if survey.direct}
|
||||
<a href="surveys/{survey.id}/live" class="btn btn-danger ms-1 float-end" title="Aller au direct"><i class="bi bi-film"></i></a>
|
||||
{/if}
|
||||
<div class="d-flex align-items-center">
|
||||
<h2>
|
||||
<a href="surveys/" class="text-muted" style="text-decoration: none"><</a>
|
||||
{survey.title}
|
||||
</h2>
|
||||
<SurveyBadge class="ms-2" {survey} />
|
||||
{/if}
|
||||
<div class="d-flex align-items-center">
|
||||
<h2>
|
||||
<a href="surveys/" class="text-muted" style="text-decoration: none"><</a>
|
||||
{survey.title}
|
||||
</h2>
|
||||
<SurveyBadge class="ms-2" {survey} />
|
||||
</div>
|
||||
|
||||
{#if $user && $user.is_admin && edit}
|
||||
<SurveyAdmin {survey} on:saved={() => edit = false} />
|
||||
{/if}
|
||||
|
||||
{#await getQuestions(survey.id)}
|
||||
<div class="text-center">
|
||||
<div class="spinner-border text-primary mx-3" role="status"></div>
|
||||
<span>Chargement des questions …</span>
|
||||
</div>
|
||||
|
||||
{#if $user && $user.is_admin && edit}
|
||||
<SurveyAdmin {survey} on:saved={() => edit = false} />
|
||||
{:then questions}
|
||||
<SurveyQuestions {survey} {questions} />
|
||||
{:catch error}
|
||||
<div class="row mt-5">
|
||||
<div class="d-none d-sm-block col-sm">
|
||||
<hr>
|
||||
</div>
|
||||
<h3 class="col-sm-auto text-center text-muted mb-3"><label for="askquestion">Ce questionnaire n'est pas accessible</label></h3>
|
||||
<div class="d-none d-sm-block col-sm">
|
||||
<hr>
|
||||
</div>
|
||||
</div>
|
||||
{#if survey.direct != null}
|
||||
<div class="alert alert-warning">
|
||||
<strong><a href="surveys/{survey.id}/live">Cliquez ici pour accéder au direct</a>.</strong> Il s'agit d'un questionnaire en direct, le questionnaire n'est pas accessible sur cette page.
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#await getQuestions(survey.id)}
|
||||
<div class="text-center">
|
||||
<div class="spinner-border text-primary mx-3" role="status"></div>
|
||||
<span>Chargement des questions …</span>
|
||||
</div>
|
||||
{:then questions}
|
||||
<SurveyQuestions {survey} {questions} />
|
||||
{:catch error}
|
||||
<div class="row mt-5">
|
||||
<div class="d-none d-sm-block col-sm">
|
||||
<hr>
|
||||
</div>
|
||||
<h3 class="col-sm-auto text-center text-muted mb-3"><label for="askquestion">Ce questionnaire n'est pas accessible</label></h3>
|
||||
<div class="d-none d-sm-block col-sm">
|
||||
<hr>
|
||||
</div>
|
||||
</div>
|
||||
{#if survey.direct != null}
|
||||
<div class="alert alert-warning">
|
||||
<strong><a href="surveys/{survey.id}/live">Cliquez ici pour accéder au direct</a>.</strong> Il s'agit d'un questionnaire en direct, le questionnaire n'est pas accessible sur cette page.
|
||||
</div>
|
||||
{/if}
|
||||
{/await}
|
||||
{/await}
|
||||
|
|
|
|||
8
ui/src/routes/surveys/[sid]/admin/+page.js
Normal file
8
ui/src/routes/surveys/[sid]/admin/+page.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
export async function load({ parent, params }) {
|
||||
const stuff = await parent();
|
||||
|
||||
return {
|
||||
survey: stuff.survey,
|
||||
sid: params.sid,
|
||||
};
|
||||
}
|
||||
|
|
@ -1,14 +1,3 @@
|
|||
<script context="module">
|
||||
export async function load({ params, stuff }) {
|
||||
return {
|
||||
props: {
|
||||
surveyP: stuff.survey,
|
||||
sid: params.sid,
|
||||
},
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
import { user } from '$lib/stores/user';
|
||||
import CorrectionPieChart from '$lib/components/CorrectionPieChart.svelte';
|
||||
|
|
@ -21,28 +10,24 @@
|
|||
import { getQuestion, getQuestions, Question } from '$lib/questions';
|
||||
import { getUsers } from '$lib/users';
|
||||
|
||||
export let surveyP;
|
||||
export let sid;
|
||||
export let data;
|
||||
let survey;
|
||||
let req_questions;
|
||||
|
||||
surveyP.then((s) => {
|
||||
survey = s;
|
||||
$: {
|
||||
survey = data.survey;
|
||||
updateQuestions();
|
||||
if (survey.direct !== null) {
|
||||
wsconnect();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateSurvey() {
|
||||
surveyP = getSurvey(survey.id);
|
||||
surveyP.then((s) => {
|
||||
survey = s;
|
||||
updateQuestions();
|
||||
if (survey.direct !== null) {
|
||||
wsconnect();
|
||||
}
|
||||
});
|
||||
async function updateSurvey() {
|
||||
survey = await getSurvey(survey.id);
|
||||
updateQuestions();
|
||||
if (survey.direct !== null) {
|
||||
wsconnect();
|
||||
}
|
||||
}
|
||||
|
||||
function updateQuestions() {
|
||||
|
|
@ -162,7 +147,7 @@
|
|||
function wsconnect() {
|
||||
if (ws !== null) return;
|
||||
|
||||
ws = new WebSocket((window.location.protocol == 'https:'?'wss://':'ws://') + window.location.host + `/api/surveys/${sid}/ws-admin`);
|
||||
ws = new WebSocket((window.location.protocol == 'https:'?'wss://':'ws://') + window.location.host + `/api/surveys/${data.sid}/ws-admin`);
|
||||
|
||||
ws.addEventListener("open", () => {
|
||||
ws_up = true;
|
||||
|
|
@ -233,472 +218,469 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
{#await surveyP then survey}
|
||||
{#if $user && $user.is_admin}
|
||||
<StartStopLiveSurvey
|
||||
{survey}
|
||||
class="ms-1 float-end"
|
||||
on:update={() => updateSurvey()}
|
||||
on:end={() => { if (confirm("Sûr ?")) ws.send('{"action":"end"}') }}
|
||||
/>
|
||||
<a href="surveys/{survey.id}/responses" class="btn btn-success ms-1 float-end" title="Voir les réponses"><i class="bi bi-files"></i></a>
|
||||
{/if}
|
||||
<div class="d-flex align-items-center">
|
||||
<h2>
|
||||
<a href="surveys/" class="text-muted" style="text-decoration: none"><</a>
|
||||
{survey.title}
|
||||
<small class="text-muted">
|
||||
Administration
|
||||
</small>
|
||||
{#if asks.length}
|
||||
<a href="surveys/{sid}/admin#questions_part">
|
||||
<i class="bi bi-patch-question-fill text-danger"></i>
|
||||
</a>
|
||||
{/if}
|
||||
</h2>
|
||||
{#if survey.direct !== null}
|
||||
<div
|
||||
class="badge rounded-pill ms-2"
|
||||
class:bg-success={ws_up}
|
||||
class:bg-danger={!ws_up}
|
||||
>
|
||||
{#if ws_up}Connecté{:else}Déconnecté{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<SurveyBadge
|
||||
class="mx-2"
|
||||
{survey}
|
||||
/>
|
||||
{#if $user && $user.is_admin}
|
||||
<StartStopLiveSurvey
|
||||
{survey}
|
||||
class="ms-1 float-end"
|
||||
on:update={() => updateSurvey()}
|
||||
on:end={() => { if (confirm("Sûr ?")) ws.send('{"action":"end"}') }}
|
||||
/>
|
||||
<a href="surveys/{survey.id}/responses" class="btn btn-success ms-1 float-end" title="Voir les réponses"><i class="bi bi-files"></i></a>
|
||||
{/if}
|
||||
<div class="d-flex align-items-center">
|
||||
<h2>
|
||||
<a href="surveys/" class="text-muted" style="text-decoration: none"><</a>
|
||||
{survey.title}
|
||||
<small class="text-muted">
|
||||
Administration
|
||||
</small>
|
||||
{#if asks.length}
|
||||
<a href="surveys/{data.sid}/admin#questions_part">
|
||||
<i class="bi bi-patch-question-fill text-danger"></i>
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if survey.direct === null}
|
||||
<SurveyAdmin
|
||||
{survey}
|
||||
on:saved={updateSurvey}
|
||||
/>
|
||||
</h2>
|
||||
{#if survey.direct !== null}
|
||||
<div
|
||||
class="badge rounded-pill ms-2"
|
||||
class:bg-success={ws_up}
|
||||
class:bg-danger={!ws_up}
|
||||
>
|
||||
{#if ws_up}Connecté{:else}Déconnecté{/if}
|
||||
</div>
|
||||
{:else}
|
||||
{#await req_questions}
|
||||
<div class="text-center">
|
||||
<div class="spinner-border text-primary mx-3" role="status"></div>
|
||||
<span>Chargement des questions …</span>
|
||||
</div>
|
||||
{:then questions}
|
||||
<div class="card my-3">
|
||||
<table class="table table-hover table-striped mb-0">
|
||||
<thead>
|
||||
<SurveyBadge
|
||||
class="mx-2"
|
||||
{survey}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if survey.direct === null}
|
||||
<SurveyAdmin
|
||||
{survey}
|
||||
on:saved={updateSurvey}
|
||||
/>
|
||||
{:else}
|
||||
{#await req_questions}
|
||||
<div class="text-center">
|
||||
<div class="spinner-border text-primary mx-3" role="status"></div>
|
||||
<span>Chargement des questions …</span>
|
||||
</div>
|
||||
{:then questions}
|
||||
<div class="card my-3">
|
||||
<table class="table table-hover table-striped mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
Question
|
||||
{#if timer_end}
|
||||
<div class="input-group input-group-sm float-end" style="max-width: 150px;">
|
||||
<input
|
||||
type="number"
|
||||
class="form-control"
|
||||
disabled
|
||||
value={timer_remain}
|
||||
>
|
||||
<span class="input-group-text">s</span>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="input-group input-group-sm float-end" style="max-width: 150px;">
|
||||
<input
|
||||
type="number"
|
||||
class="form-control"
|
||||
bind:value={timer}
|
||||
placeholder="Valeur du timer"
|
||||
>
|
||||
<span class="input-group-text">s</span>
|
||||
</div>
|
||||
{/if}
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-info ms-1"
|
||||
on:click={updateQuestions}
|
||||
title="Rafraîchir les questions"
|
||||
>
|
||||
<i class="bi bi-arrow-counterclockwise"></i>
|
||||
</button>
|
||||
</th>
|
||||
<th>
|
||||
Réponses
|
||||
</th>
|
||||
<th>
|
||||
Actions
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-primary"
|
||||
disabled={!current_question || !ws_up}
|
||||
on:click={() => { ws.send('{"action":"pause"}')} }
|
||||
title="Passer sur une scène sans question"
|
||||
>
|
||||
<i class="bi bi-pause-fill"></i>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm"
|
||||
class:btn-outline-success={!next_corrected}
|
||||
class:btn-success={next_corrected}
|
||||
on:click={() => { next_corrected = !next_corrected } }
|
||||
title="La prochaine question est affichée corrigée"
|
||||
>
|
||||
<i class="bi bi-eye"></i>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-info"
|
||||
on:click={() => { edit_question = new Question({ id_survey: survey.id }) } }
|
||||
title="Ajouter une question"
|
||||
>
|
||||
<i class="bi bi-plus"></i>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-outline-danger"
|
||||
on:click={() => { fetch('api/cache', {method: 'DELETE'}) } }
|
||||
title="Vider les caches"
|
||||
>
|
||||
<i class="bi bi-bandaid-fill"></i>
|
||||
</button>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each questions as question (question.id)}
|
||||
<tr>
|
||||
<th>
|
||||
Question
|
||||
{#if timer_end}
|
||||
<div class="input-group input-group-sm float-end" style="max-width: 150px;">
|
||||
<input
|
||||
type="number"
|
||||
class="form-control"
|
||||
disabled
|
||||
value={timer_remain}
|
||||
>
|
||||
<span class="input-group-text">s</span>
|
||||
</div>
|
||||
<td>
|
||||
{#if responses[question.id]}
|
||||
<a href="surveys/{data.sid}/admin#q{question.id}_res">
|
||||
{question.title}
|
||||
</a>
|
||||
{:else}
|
||||
<div class="input-group input-group-sm float-end" style="max-width: 150px;">
|
||||
<input
|
||||
type="number"
|
||||
class="form-control"
|
||||
bind:value={timer}
|
||||
placeholder="Valeur du timer"
|
||||
>
|
||||
<span class="input-group-text">s</span>
|
||||
</div>
|
||||
{question.title}
|
||||
{/if}
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-info ms-1"
|
||||
on:click={updateQuestions}
|
||||
title="Rafraîchir les questions"
|
||||
>
|
||||
<i class="bi bi-arrow-counterclockwise"></i>
|
||||
</button>
|
||||
</th>
|
||||
<th>
|
||||
Réponses
|
||||
</th>
|
||||
<th>
|
||||
Actions
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-primary"
|
||||
disabled={!current_question || !ws_up}
|
||||
on:click={() => { ws.send('{"action":"pause"}')} }
|
||||
title="Passer sur une scène sans question"
|
||||
>
|
||||
<i class="bi bi-pause-fill"></i>
|
||||
</button>
|
||||
</td>
|
||||
<td>
|
||||
{#if responses[question.id]}
|
||||
{Object.keys(responses[question.id]).length}
|
||||
{:else}
|
||||
0
|
||||
{/if}
|
||||
{#if wsstats}/ {wsstats.nb_clients}{/if}
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm"
|
||||
class:btn-outline-success={!next_corrected}
|
||||
class:btn-primary={!next_corrected}
|
||||
class:btn-success={next_corrected}
|
||||
on:click={() => { next_corrected = !next_corrected } }
|
||||
title="La prochaine question est affichée corrigée"
|
||||
disabled={(question.id === current_question && next_corrected == corrected) || !ws_up}
|
||||
on:click={() => { ws.send('{"action":"new_question", "corrected": ' + next_corrected + ', "timer": 0, "question":' + question.id + '}')} }
|
||||
>
|
||||
<i class="bi bi-eye"></i>
|
||||
<i class="bi bi-play-fill"></i>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-danger"
|
||||
disabled={question.id === current_question || !ws_up}
|
||||
on:click={() => { ws.send('{"action":"new_question", "corrected": ' + next_corrected + ', "timer": ' + timer * 1000 + ',"question":' + question.id + '}')} }
|
||||
>
|
||||
<i class="bi bi-stopwatch-fill"></i>
|
||||
</button>
|
||||
<a
|
||||
href="/surveys/{survey.id}/responses/{question.id}"
|
||||
target="_blank"
|
||||
type="button"
|
||||
class="btn btn-sm btn-success"
|
||||
>
|
||||
<i class="bi bi-files"></i>
|
||||
</a>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-info"
|
||||
on:click={() => { edit_question = new Question({ id_survey: survey.id }) } }
|
||||
title="Ajouter une question"
|
||||
disabled={question.id === current_question}
|
||||
on:click={() => { getQuestion(question.id).then((q) => {edit_question = q})} }
|
||||
>
|
||||
<i class="bi bi-plus"></i>
|
||||
<i class="bi bi-pencil"></i>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-outline-danger"
|
||||
on:click={() => { fetch('api/cache', {method: 'DELETE'}) } }
|
||||
title="Vider les caches"
|
||||
>
|
||||
<i class="bi bi-bandaid-fill"></i>
|
||||
</button>
|
||||
</th>
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each questions as question (question.id)}
|
||||
<tr>
|
||||
<td>
|
||||
{#if responses[question.id]}
|
||||
<a href="surveys/{sid}/admin#q{question.id}_res">
|
||||
{question.title}
|
||||
</a>
|
||||
{:else}
|
||||
{question.title}
|
||||
{/if}
|
||||
</td>
|
||||
<td>
|
||||
{#if responses[question.id]}
|
||||
{Object.keys(responses[question.id]).length}
|
||||
{:else}
|
||||
0
|
||||
{/if}
|
||||
{#if wsstats}/ {wsstats.nb_clients}{/if}
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm"
|
||||
class:btn-primary={!next_corrected}
|
||||
class:btn-success={next_corrected}
|
||||
disabled={(question.id === current_question && next_corrected == corrected) || !ws_up}
|
||||
on:click={() => { ws.send('{"action":"new_question", "corrected": ' + next_corrected + ', "timer": 0, "question":' + question.id + '}')} }
|
||||
>
|
||||
<i class="bi bi-play-fill"></i>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-danger"
|
||||
disabled={question.id === current_question || !ws_up}
|
||||
on:click={() => { ws.send('{"action":"new_question", "corrected": ' + next_corrected + ', "timer": ' + timer * 1000 + ',"question":' + question.id + '}')} }
|
||||
>
|
||||
<i class="bi bi-stopwatch-fill"></i>
|
||||
</button>
|
||||
<a
|
||||
href="/surveys/{survey.id}/responses/{question.id}"
|
||||
target="_blank"
|
||||
type="button"
|
||||
class="btn btn-sm btn-success"
|
||||
>
|
||||
<i class="bi bi-files"></i>
|
||||
</a>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-info"
|
||||
disabled={question.id === current_question}
|
||||
on:click={() => { getQuestion(question.id).then((q) => {edit_question = q})} }
|
||||
>
|
||||
<i class="bi bi-pencil"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{/await}
|
||||
{#if edit_question !== null}
|
||||
<QuestionForm
|
||||
{survey}
|
||||
edit
|
||||
question={edit_question}
|
||||
on:delete={() => deleteQuestion(edit_question)}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{/await}
|
||||
{#if edit_question !== null}
|
||||
<QuestionForm
|
||||
{survey}
|
||||
edit
|
||||
question={edit_question}
|
||||
on:delete={() => deleteQuestion(edit_question)}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<hr>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-info ms-1 float-end"
|
||||
on:click={() => { ws.send('{"action":"get_asks", "value": ""}'); asks = []; }}
|
||||
title="Rafraîchir les réponses"
|
||||
>
|
||||
<i class="bi bi-arrow-counterclockwise"></i>
|
||||
<i class="bi bi-question-diamond"></i>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-light ms-1 float-end"
|
||||
on:click={() => { ws.send('{"action":"get_asks", "value": "unanswered"}'); asks = []; }}
|
||||
title="Rafraîchir les réponses, en rapportant les réponses déjà répondues"
|
||||
>
|
||||
<i class="bi bi-arrow-counterclockwise"></i>
|
||||
<i class="bi bi-question-diamond"></i>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-success float-end"
|
||||
title="Tout marqué comme répondu"
|
||||
on:click={() => { ws.send('{"action":"mark_answered", "value": "all"}'); asks = [] }}
|
||||
>
|
||||
<i class="bi bi-check-all"></i>
|
||||
</button>
|
||||
<h3 id="questions_part">
|
||||
Questions
|
||||
{#if asks.length}
|
||||
<small class="text-muted">
|
||||
{asks.length} question{#if asks.length > 1}s{/if}
|
||||
</small>
|
||||
{/if}
|
||||
</h3>
|
||||
<hr>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-info ms-1 float-end"
|
||||
on:click={() => { ws.send('{"action":"get_asks", "value": ""}'); asks = []; }}
|
||||
title="Rafraîchir les réponses"
|
||||
>
|
||||
<i class="bi bi-arrow-counterclockwise"></i>
|
||||
<i class="bi bi-question-diamond"></i>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-light ms-1 float-end"
|
||||
on:click={() => { ws.send('{"action":"get_asks", "value": "unanswered"}'); asks = []; }}
|
||||
title="Rafraîchir les réponses, en rapportant les réponses déjà répondues"
|
||||
>
|
||||
<i class="bi bi-arrow-counterclockwise"></i>
|
||||
<i class="bi bi-question-diamond"></i>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-success float-end"
|
||||
title="Tout marqué comme répondu"
|
||||
on:click={() => { ws.send('{"action":"mark_answered", "value": "all"}'); asks = [] }}
|
||||
>
|
||||
<i class="bi bi-check-all"></i>
|
||||
</button>
|
||||
<h3 id="questions_part">
|
||||
Questions
|
||||
{#if asks.length}
|
||||
{#each asks as ask (ask.id)}
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
<p class="card-text">
|
||||
{ask.content}
|
||||
</p>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-success float-end"
|
||||
title="Marqué comme répondu"
|
||||
on:click={() => { ws.send('{"action":"mark_answered", "question": ' + ask.id + '}'); asks = asks.filter((e) => e.id != ask.id) }}
|
||||
>
|
||||
<i class="bi bi-check"></i>
|
||||
</button>
|
||||
Par
|
||||
<a href="users/{ask.userid}" target="_blank">
|
||||
{#if users && users[ask.userid]}
|
||||
{users[ask.userid].login}
|
||||
{:else}
|
||||
{ask.userid}
|
||||
{/if}
|
||||
</a>
|
||||
<small class="text-muted">
|
||||
{asks.length} question{#if asks.length > 1}s{/if}
|
||||
</small>
|
||||
{/if}
|
||||
</h3>
|
||||
{#if asks.length}
|
||||
{#each asks as ask (ask.id)}
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
<p class="card-text">
|
||||
{ask.content}
|
||||
</p>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-success float-end"
|
||||
title="Marqué comme répondu"
|
||||
on:click={() => { ws.send('{"action":"mark_answered", "question": ' + ask.id + '}'); asks = asks.filter((e) => e.id != ask.id) }}
|
||||
>
|
||||
<i class="bi bi-check"></i>
|
||||
</button>
|
||||
Par
|
||||
<a href="users/{ask.userid}" target="_blank">
|
||||
{#if users && users[ask.userid]}
|
||||
{users[ask.userid].login}
|
||||
{:else}
|
||||
{ask.userid}
|
||||
{/if}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
{:else}
|
||||
<div class="text-center text-muted">
|
||||
Pas de question pour l'instant.
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<hr>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-info ms-1 float-end"
|
||||
on:click={() => { ws.send('{"action":"get_responses"}') }}
|
||||
title="Rafraîchir les réponses"
|
||||
>
|
||||
<i class="bi bi-arrow-counterclockwise"></i>
|
||||
<i class="bi bi-card-checklist"></i>
|
||||
</button>
|
||||
<h3>
|
||||
Réponses
|
||||
</h3>
|
||||
{#if Object.keys(responses).length}
|
||||
{#each Object.keys(responses) as q, qid (qid)}
|
||||
{#await req_questions then questions}
|
||||
{#each questions as question}
|
||||
{#if question.id == q}
|
||||
<h4 id="q{question.id}_res">
|
||||
{question.title}
|
||||
</h4>
|
||||
{#if question.kind == 'ucq'}
|
||||
{#await question.getProposals()}
|
||||
<div class="text-center">
|
||||
<div class="spinner-border text-primary mx-3" role="status"></div>
|
||||
<span>Chargement des propositions …</span>
|
||||
</div>
|
||||
{:then proposals}
|
||||
{#if current_question == question.id}
|
||||
<CorrectionPieChart
|
||||
{question}
|
||||
{proposals}
|
||||
data={graph_data}
|
||||
/>
|
||||
{:else}
|
||||
<CorrectionPieChart
|
||||
{question}
|
||||
/>
|
||||
{/if}
|
||||
<div class="card mb-4">
|
||||
<table class="table table-sm table-striped table-hover mb-0">
|
||||
<tbody>
|
||||
{#each proposals as proposal (proposal.id)}
|
||||
<tr>
|
||||
<td>
|
||||
{proposal.label}
|
||||
</td>
|
||||
<td>
|
||||
{responsesbyid[q].filter((e) => e == proposal.id.toString()).length}/{responsesbyid[q].length}
|
||||
</td>
|
||||
<td>
|
||||
{Math.trunc(responsesbyid[q].filter((e) => e == proposal.id.toString()).length / responsesbyid[q].length * 1000)/10} %
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{/await}
|
||||
{:else if question.kind == 'mcq'}
|
||||
{#await question.getProposals()}
|
||||
<div class="text-center">
|
||||
<div class="spinner-border text-primary mx-3" role="status"></div>
|
||||
<span>Chargement des propositions …</span>
|
||||
</div>
|
||||
{:then proposals}
|
||||
{#if current_question == question.id}
|
||||
<CorrectionPieChart
|
||||
{question}
|
||||
{proposals}
|
||||
data={graph_data}
|
||||
/>
|
||||
{:else}
|
||||
<CorrectionPieChart
|
||||
{question}
|
||||
/>
|
||||
{/if}
|
||||
<div class="card mb-4">
|
||||
<table class="table table-sm table-striped table-hover mb-0">
|
||||
<tbody>
|
||||
{#each proposals as proposal (proposal.id)}
|
||||
<tr>
|
||||
<td>
|
||||
{proposal.label}
|
||||
</td>
|
||||
<td>
|
||||
{responsesbyid[q].filter((e) => e.indexOf(proposal.id.toString()) >= 0).length}/{responsesbyid[q].length}
|
||||
</td>
|
||||
<td>
|
||||
{Math.trunc(responsesbyid[q].filter((e) => e.indexOf(proposal.id.toString()) >= 0).length / responsesbyid[q].length * 1000)/10} %
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{/await}
|
||||
{:else if question.kind && question.kind.startsWith('list')}
|
||||
<ListInputResponses
|
||||
responses={responses[q]}
|
||||
{users}
|
||||
/>
|
||||
{:else}
|
||||
<div class="card mb-4">
|
||||
<ul class="list-group list-group-flush">
|
||||
{#each Object.keys(responses[q]) as user, rid (rid)}
|
||||
<li class="list-group-item list-group-item-action d-flex justify-content-between align-items-center">
|
||||
<span>
|
||||
{responses[q][user]}
|
||||
</span>
|
||||
<a href="users/{user}" target="_blank" class="badge bg-dark rounded-pill">
|
||||
{#if users && users[user]}
|
||||
{users[user].login}
|
||||
{:else}
|
||||
{user}
|
||||
{/if}
|
||||
</a>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
{/each}
|
||||
{/await}
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
<hr>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-info ms-1 float-end"
|
||||
on:click={() => { ws.send('{"action":"get_stats"}') }}
|
||||
title="Rafraîchir les stats"
|
||||
>
|
||||
<i class="bi bi-arrow-counterclockwise"></i>
|
||||
<i class="bi bi-123"></i>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-primary ms-1 float-end"
|
||||
title="Rafraîchir la liste des utilisateurs"
|
||||
on:click={updateUsers}
|
||||
>
|
||||
<i class="bi bi-arrow-clockwise"></i>
|
||||
<i class="bi bi-people"></i>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-warning ms-1 float-end"
|
||||
on:click={() => { scroll_states = {}; ws.send('{"action":"where_are_you"}')} }
|
||||
title="Rapporter l'avancement"
|
||||
>
|
||||
<i class="bi bi-geo-fill"></i>
|
||||
</button>
|
||||
<h3 id="users">
|
||||
Connectés
|
||||
{#if wsstats}
|
||||
<small class="text-muted">{wsstats.nb_clients} utilisateurs</small>
|
||||
{/if}
|
||||
{#if scroll_mean}
|
||||
<small
|
||||
class:text-danger={scroll_mean >= 0 && scroll_mean < 0.2}
|
||||
class:text-warning={scroll_mean >= 0.2 && scroll_mean < 0.6}
|
||||
class:text-info={scroll_mean >= 0.6 && scroll_mean < 0.9}
|
||||
class:text-success={scroll_mean >= 0.9}
|
||||
>Avancement global : {Math.trunc(scroll_mean*10000)/100} %</small>
|
||||
{/if}
|
||||
</h3>
|
||||
{#if wsstats && wsstats.users}
|
||||
<div class="row row-cols-5 py-3">
|
||||
{#each wsstats.users as user, lid (lid)}
|
||||
<div class="col">
|
||||
<div class="card">
|
||||
<img alt="{user.login}" src="//photos.cri.epita.fr/thumb/{user.login}" class="card-img-top">
|
||||
<div class="card-footer text-center text-truncate p-0">
|
||||
<a href="users/{user.login}" target="_blank">
|
||||
{user.login}
|
||||
</a>
|
||||
</div>
|
||||
{#if user.myscroll != null}
|
||||
<div
|
||||
class="card-footer py-0 px-1"
|
||||
class:bg-danger={user.myscroll >= 0 && user.myscroll < 0.2}
|
||||
class:bg-warning={user.myscroll >= 0.2 && user.myscroll < 0.6}
|
||||
class:bg-info={user.myscroll >= 0.6 && user.myscroll < 0.9}
|
||||
class:bg-success={user.myscroll >= 0.9}
|
||||
>
|
||||
Avancement : {Math.trunc(user.myscroll*10000)/100} %
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
{:else}
|
||||
<div class="text-center text-muted">
|
||||
Pas de question pour l'instant.
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<hr>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-info ms-1 float-end"
|
||||
on:click={() => { ws.send('{"action":"get_responses"}') }}
|
||||
title="Rafraîchir les réponses"
|
||||
>
|
||||
<i class="bi bi-arrow-counterclockwise"></i>
|
||||
<i class="bi bi-card-checklist"></i>
|
||||
</button>
|
||||
<h3>
|
||||
Réponses
|
||||
</h3>
|
||||
{#if Object.keys(responses).length}
|
||||
{#each Object.keys(responses) as q, qid (qid)}
|
||||
{#await req_questions then questions}
|
||||
{#each questions as question}
|
||||
{#if question.id == q}
|
||||
<h4 id="q{question.id}_res">
|
||||
{question.title}
|
||||
</h4>
|
||||
{#if question.kind == 'ucq'}
|
||||
{#await question.getProposals()}
|
||||
<div class="text-center">
|
||||
<div class="spinner-border text-primary mx-3" role="status"></div>
|
||||
<span>Chargement des propositions …</span>
|
||||
</div>
|
||||
{:then proposals}
|
||||
{#if current_question == question.id}
|
||||
<CorrectionPieChart
|
||||
{question}
|
||||
{proposals}
|
||||
data={graph_data}
|
||||
/>
|
||||
{:else}
|
||||
<CorrectionPieChart
|
||||
{question}
|
||||
/>
|
||||
{/if}
|
||||
<div class="card mb-4">
|
||||
<table class="table table-sm table-striped table-hover mb-0">
|
||||
<tbody>
|
||||
{#each proposals as proposal (proposal.id)}
|
||||
<tr>
|
||||
<td>
|
||||
{proposal.label}
|
||||
</td>
|
||||
<td>
|
||||
{responsesbyid[q].filter((e) => e == proposal.id.toString()).length}/{responsesbyid[q].length}
|
||||
</td>
|
||||
<td>
|
||||
{Math.trunc(responsesbyid[q].filter((e) => e == proposal.id.toString()).length / responsesbyid[q].length * 1000)/10} %
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{/await}
|
||||
{:else if question.kind == 'mcq'}
|
||||
{#await question.getProposals()}
|
||||
<div class="text-center">
|
||||
<div class="spinner-border text-primary mx-3" role="status"></div>
|
||||
<span>Chargement des propositions …</span>
|
||||
</div>
|
||||
{:then proposals}
|
||||
{#if current_question == question.id}
|
||||
<CorrectionPieChart
|
||||
{question}
|
||||
{proposals}
|
||||
data={graph_data}
|
||||
/>
|
||||
{:else}
|
||||
<CorrectionPieChart
|
||||
{question}
|
||||
/>
|
||||
{/if}
|
||||
<div class="card mb-4">
|
||||
<table class="table table-sm table-striped table-hover mb-0">
|
||||
<tbody>
|
||||
{#each proposals as proposal (proposal.id)}
|
||||
<tr>
|
||||
<td>
|
||||
{proposal.label}
|
||||
</td>
|
||||
<td>
|
||||
{responsesbyid[q].filter((e) => e.indexOf(proposal.id.toString()) >= 0).length}/{responsesbyid[q].length}
|
||||
</td>
|
||||
<td>
|
||||
{Math.trunc(responsesbyid[q].filter((e) => e.indexOf(proposal.id.toString()) >= 0).length / responsesbyid[q].length * 1000)/10} %
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{/await}
|
||||
{:else if question.kind && question.kind.startsWith('list')}
|
||||
<ListInputResponses
|
||||
responses={responses[q]}
|
||||
{users}
|
||||
/>
|
||||
{:else}
|
||||
<div class="card mb-4">
|
||||
<ul class="list-group list-group-flush">
|
||||
{#each Object.keys(responses[q]) as user, rid (rid)}
|
||||
<li class="list-group-item list-group-item-action d-flex justify-content-between align-items-center">
|
||||
<span>
|
||||
{responses[q][user]}
|
||||
</span>
|
||||
<a href="users/{user}" target="_blank" class="badge bg-dark rounded-pill">
|
||||
{#if users && users[user]}
|
||||
{users[user].login}
|
||||
{:else}
|
||||
{user}
|
||||
{/if}
|
||||
</a>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
{/each}
|
||||
{/await}
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
<hr>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-info ms-1 float-end"
|
||||
on:click={() => { ws.send('{"action":"get_stats"}') }}
|
||||
title="Rafraîchir les stats"
|
||||
>
|
||||
<i class="bi bi-arrow-counterclockwise"></i>
|
||||
<i class="bi bi-123"></i>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-primary ms-1 float-end"
|
||||
title="Rafraîchir la liste des utilisateurs"
|
||||
on:click={updateUsers}
|
||||
>
|
||||
<i class="bi bi-arrow-clockwise"></i>
|
||||
<i class="bi bi-people"></i>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-warning ms-1 float-end"
|
||||
on:click={() => { scroll_states = {}; ws.send('{"action":"where_are_you"}')} }
|
||||
title="Rapporter l'avancement"
|
||||
>
|
||||
<i class="bi bi-geo-fill"></i>
|
||||
</button>
|
||||
<h3 id="users">
|
||||
Connectés
|
||||
{#if wsstats}
|
||||
<small class="text-muted">{wsstats.nb_clients} utilisateurs</small>
|
||||
{/if}
|
||||
{#if scroll_mean}
|
||||
<small
|
||||
class:text-danger={scroll_mean >= 0 && scroll_mean < 0.2}
|
||||
class:text-warning={scroll_mean >= 0.2 && scroll_mean < 0.6}
|
||||
class:text-info={scroll_mean >= 0.6 && scroll_mean < 0.9}
|
||||
class:text-success={scroll_mean >= 0.9}
|
||||
>Avancement global : {Math.trunc(scroll_mean*10000)/100} %</small>
|
||||
{/if}
|
||||
</h3>
|
||||
{#if wsstats && wsstats.users}
|
||||
<div class="row row-cols-5 py-3">
|
||||
{#each wsstats.users as user, lid (lid)}
|
||||
<div class="col">
|
||||
<div class="card">
|
||||
<img alt="{user.login}" src="//photos.cri.epita.fr/thumb/{user.login}" class="card-img-top">
|
||||
<div class="card-footer text-center text-truncate p-0">
|
||||
<a href="users/{user.login}" target="_blank">
|
||||
{user.login}
|
||||
</a>
|
||||
</div>
|
||||
{#if user.myscroll != null}
|
||||
<div
|
||||
class="card-footer py-0 px-1"
|
||||
class:bg-danger={user.myscroll >= 0 && user.myscroll < 0.2}
|
||||
class:bg-warning={user.myscroll >= 0.2 && user.myscroll < 0.6}
|
||||
class:bg-info={user.myscroll >= 0.6 && user.myscroll < 0.9}
|
||||
class:bg-success={user.myscroll >= 0.9}
|
||||
>
|
||||
Avancement : {Math.trunc(user.myscroll*10000)/100} %
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{/await}
|
||||
{/if}
|
||||
|
|
|
|||
8
ui/src/routes/surveys/[sid]/live/+page.js
Normal file
8
ui/src/routes/surveys/[sid]/live/+page.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
export async function load({ params, parent }) {
|
||||
const stuff = await parent();
|
||||
|
||||
return {
|
||||
survey: stuff.survey,
|
||||
sid: params.sid,
|
||||
};
|
||||
}
|
||||
|
|
@ -1,14 +1,3 @@
|
|||
<script context="module">
|
||||
export async function load({ params, stuff }) {
|
||||
return {
|
||||
props: {
|
||||
surveyP: stuff.survey,
|
||||
sid: params.sid,
|
||||
},
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
import { onDestroy } from 'svelte';
|
||||
|
||||
|
|
@ -18,11 +7,9 @@
|
|||
import QuestionForm from '$lib/components/QuestionForm.svelte';
|
||||
import { getQuestion } from '$lib/questions';
|
||||
|
||||
export let surveyP;
|
||||
export let sid;
|
||||
export let data;
|
||||
let survey;
|
||||
|
||||
surveyP.then((s) => survey = s);
|
||||
$: survey = data.survey;
|
||||
|
||||
let ws_up = false;
|
||||
let show_question = null;
|
||||
|
|
@ -75,7 +62,7 @@
|
|||
}
|
||||
});
|
||||
function wsconnect() {
|
||||
ws = new WebSocket((window.location.protocol == 'https:'?'wss://':'ws://') + window.location.host + `/api/surveys/${sid}/ws`);
|
||||
ws = new WebSocket((window.location.protocol == 'https:'?'wss://':'ws://') + window.location.host + `/api/surveys/${data.sid}/ws`);
|
||||
|
||||
ws.addEventListener("open", () => {
|
||||
ws_up = true;
|
||||
|
|
@ -183,120 +170,118 @@
|
|||
let corrections = null;
|
||||
</script>
|
||||
|
||||
{#await surveyP then unused}
|
||||
<div
|
||||
style={"transition: opacity 150ms ease-out; opacity: " + (displaySendInProgress?1:0)}
|
||||
class="ms-2 float-end"
|
||||
>
|
||||
<div style="position: relative; left: 25%; top: 4px">
|
||||
<div style="position: absolute">
|
||||
<i class="bi bi-save"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="spinner-border text-primary" role="status"></div>
|
||||
</div>
|
||||
{#if $user && $user.is_admin}
|
||||
<a href="surveys/{survey.id}/admin" class="btn btn-primary ms-1 float-end" title="Aller à l'interface d'administration"><i class="bi bi-pencil"></i></a>
|
||||
<a href="surveys/{survey.id}/responses" class="btn btn-success ms-1 float-end" title="Voir les réponses"><i class="bi bi-files"></i></a>
|
||||
{/if}
|
||||
<div class="d-flex align-items-center mb-3 mb-md-4 mb-lg-5">
|
||||
<h2>
|
||||
<a href="surveys/" class="text-muted" style="text-decoration: none"><</a>
|
||||
{survey.title}
|
||||
</h2>
|
||||
<div
|
||||
style={"transition: opacity 150ms ease-out; opacity: " + (displaySendInProgress?1:0)}
|
||||
class="ms-2 float-end"
|
||||
class="badge rounded-pill ms-2"
|
||||
class:bg-success={ws_up}
|
||||
class:bg-danger={!ws_up}
|
||||
>
|
||||
<div style="position: relative; left: 25%; top: 4px">
|
||||
<div style="position: absolute">
|
||||
<i class="bi bi-save"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="spinner-border text-primary" role="status"></div>
|
||||
</div>
|
||||
{#if $user && $user.is_admin}
|
||||
<a href="surveys/{survey.id}/admin" class="btn btn-primary ms-1 float-end" title="Aller à l'interface d'administration"><i class="bi bi-pencil"></i></a>
|
||||
<a href="surveys/{survey.id}/responses" class="btn btn-success ms-1 float-end" title="Voir les réponses"><i class="bi bi-files"></i></a>
|
||||
{/if}
|
||||
<div class="d-flex align-items-center mb-3 mb-md-4 mb-lg-5">
|
||||
<h2>
|
||||
<a href="surveys/" class="text-muted" style="text-decoration: none"><</a>
|
||||
{survey.title}
|
||||
</h2>
|
||||
<div
|
||||
class="badge rounded-pill ms-2"
|
||||
class:bg-success={ws_up}
|
||||
class:bg-danger={!ws_up}
|
||||
>
|
||||
{#if ws_up}Connecté{:else}Déconnecté{/if}
|
||||
</div>
|
||||
{#if ws_up}Connecté{:else}Déconnecté{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form on:submit|preventDefault={sendValue}>
|
||||
{#if show_question}
|
||||
{#await req_question}
|
||||
<div class="text-center">
|
||||
<div class="spinner-border text-primary mx-3" role="status"></div>
|
||||
<span>Chargement d'une nouvelle question …</span>
|
||||
</div>
|
||||
{:then question}
|
||||
<QuestionForm
|
||||
{survey}
|
||||
{question}
|
||||
readonly={timer >= 100 || survey.corrected}
|
||||
{corrections}
|
||||
bind:value={value}
|
||||
on:change={sendValue}
|
||||
<form on:submit|preventDefault={sendValue}>
|
||||
{#if show_question}
|
||||
{#await req_question}
|
||||
<div class="text-center">
|
||||
<div class="spinner-border text-primary mx-3" role="status"></div>
|
||||
<span>Chargement d'une nouvelle question …</span>
|
||||
</div>
|
||||
{:then question}
|
||||
<QuestionForm
|
||||
{survey}
|
||||
{question}
|
||||
readonly={timer >= 100 || survey.corrected}
|
||||
{corrections}
|
||||
bind:value={value}
|
||||
on:change={sendValue}
|
||||
>
|
||||
{#if timer_init}
|
||||
<div class="progress" style="border-radius: 0; height: 4px">
|
||||
<div class="progress-bar" class:bg-warning={timer > 85 && timer < 100} class:bg-danger={timer >= 100} role="progressbar" style="width: {timer}%"></div>
|
||||
</div>
|
||||
{/if}
|
||||
</QuestionForm>
|
||||
{#if question.kind != 'mcq' && question.kind != 'ucq' && question.kind != 'none'}
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
>
|
||||
{#if timer_init}
|
||||
<div class="progress" style="border-radius: 0; height: 4px">
|
||||
<div class="progress-bar" class:bg-warning={timer > 85 && timer < 100} class:bg-danger={timer >= 100} role="progressbar" style="width: {timer}%"></div>
|
||||
</div>
|
||||
{/if}
|
||||
</QuestionForm>
|
||||
{#if question.kind != 'mcq' && question.kind != 'ucq' && question.kind != 'none'}
|
||||
Soumettre cette réponse
|
||||
</button>
|
||||
{/if}
|
||||
{/await}
|
||||
{:else if ws_up}
|
||||
<h2 class="text-center mb-4">
|
||||
Pas de question actuellement.
|
||||
</h2>
|
||||
<form on:submit|preventDefault={askQuestion}>
|
||||
<div class="row">
|
||||
<div class="d-none d-sm-block col-sm">
|
||||
<hr>
|
||||
</div>
|
||||
<h3 class="col-sm-auto text-center text-muted mb-3"><label for="askquestion">Vous avez une question ?</label></h3>
|
||||
<div class="d-none d-sm-block col-sm">
|
||||
<hr>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="offset-md-1 col-md-10 offset-lg-2 col-lg-8 offset-xl-3 col-xl-6 mb-4">
|
||||
<div class="input-group">
|
||||
<textarea
|
||||
id="askquestion"
|
||||
class="form-control"
|
||||
bind:value={myQuestion}
|
||||
autofocus
|
||||
placeholder="Remarques, soucis, choses pas claires ? Levez la main ou écrivez ici !"
|
||||
></textarea>
|
||||
<button
|
||||
class="d-sm-none btn btn-primary"
|
||||
disabled={!myQuestion || submitQuestionInProgress}
|
||||
>
|
||||
{#if submitQuestionInProgress}
|
||||
<div class="spinner-border spinner-border-sm me-1" role="status"></div>
|
||||
{/if}
|
||||
Poser cette question
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{#if myQuestion}
|
||||
<div class="d-none d-sm-block text-center mb-4">
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
disabled={submitQuestionInProgress}
|
||||
>
|
||||
Soumettre cette réponse
|
||||
{#if submitQuestionInProgress}
|
||||
<div class="spinner-border spinner-border-sm me-1" role="status"></div>
|
||||
{/if}
|
||||
Poser cette question
|
||||
</button>
|
||||
{/if}
|
||||
{/await}
|
||||
{:else if ws_up}
|
||||
<h2 class="text-center mb-4">
|
||||
Pas de question actuellement.
|
||||
</h2>
|
||||
<form on:submit|preventDefault={askQuestion}>
|
||||
<div class="row">
|
||||
<div class="d-none d-sm-block col-sm">
|
||||
<hr>
|
||||
</div>
|
||||
<h3 class="col-sm-auto text-center text-muted mb-3"><label for="askquestion">Vous avez une question ?</label></h3>
|
||||
<div class="d-none d-sm-block col-sm">
|
||||
<hr>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="offset-md-1 col-md-10 offset-lg-2 col-lg-8 offset-xl-3 col-xl-6 mb-4">
|
||||
<div class="input-group">
|
||||
<textarea
|
||||
id="askquestion"
|
||||
class="form-control"
|
||||
bind:value={myQuestion}
|
||||
autofocus
|
||||
placeholder="Remarques, soucis, choses pas claires ? Levez la main ou écrivez ici !"
|
||||
></textarea>
|
||||
<button
|
||||
class="d-sm-none btn btn-primary"
|
||||
disabled={!myQuestion || submitQuestionInProgress}
|
||||
>
|
||||
{#if submitQuestionInProgress}
|
||||
<div class="spinner-border spinner-border-sm me-1" role="status"></div>
|
||||
{/if}
|
||||
Poser cette question
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{#if myQuestion}
|
||||
<div class="d-none d-sm-block text-center mb-4">
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
disabled={submitQuestionInProgress}
|
||||
>
|
||||
{#if submitQuestionInProgress}
|
||||
<div class="spinner-border spinner-border-sm me-1" role="status"></div>
|
||||
{/if}
|
||||
Poser cette question
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
</form>
|
||||
{:else}
|
||||
<h2 class="text-center">
|
||||
La session est terminée. <small class="text-muted">On se retrouve une prochaine fois…</small>
|
||||
</h2>
|
||||
{/if}
|
||||
</form>
|
||||
{/await}
|
||||
{/if}
|
||||
</form>
|
||||
{:else}
|
||||
<h2 class="text-center">
|
||||
La session est terminée. <small class="text-muted">On se retrouve une prochaine fois…</small>
|
||||
</h2>
|
||||
{/if}
|
||||
</form>
|
||||
|
|
|
|||
7
ui/src/routes/surveys/[sid]/responses/+page.js
Normal file
7
ui/src/routes/surveys/[sid]/responses/+page.js
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
export async function load({ parent }) {
|
||||
const stuff = await parent();
|
||||
|
||||
return {
|
||||
survey: stuff.survey,
|
||||
};
|
||||
}
|
||||
|
|
@ -1,32 +1,20 @@
|
|||
<script context="module">
|
||||
import CorrectionPieChart from '$lib/components/CorrectionPieChart.svelte';
|
||||
import { getSurvey } from '$lib/surveys';
|
||||
import { getUsers } from '$lib/users';
|
||||
|
||||
export async function load({ params, stuff }) {
|
||||
return {
|
||||
props: {
|
||||
surveyP: stuff.survey,
|
||||
},
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
import { user } from '$lib/stores/user';
|
||||
import CorrectionPieChart from '$lib/components/CorrectionPieChart.svelte';
|
||||
import StartStopLiveSurvey from '$lib/components/StartStopLiveSurvey.svelte';
|
||||
import SurveyAdmin from '$lib/components/SurveyAdmin.svelte';
|
||||
import SurveyBadge from '$lib/components/SurveyBadge.svelte';
|
||||
import SurveyQuestions from '$lib/components/SurveyQuestions.svelte';
|
||||
import { getQuestions } from '$lib/questions';
|
||||
import { getUsers } from '$lib/users';
|
||||
|
||||
export let surveyP;
|
||||
let usersP = null;
|
||||
surveyP.then((s) => {
|
||||
usersP = getUsers(s.promo, s.group);
|
||||
})
|
||||
export let data;
|
||||
let survey;
|
||||
let usersP;
|
||||
$: survey = data.survey;
|
||||
$: usersP = getUsers(data.survey.promo, data.survey.group);
|
||||
let edit = false;
|
||||
let exportview = false;
|
||||
let exportview_list = false;
|
||||
|
|
@ -41,122 +29,120 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
{#await surveyP then survey}
|
||||
{#if $user && $user.is_admin}
|
||||
<button class="btn btn-primary ms-1 float-end" on:click={() => { edit = !edit; } } title="Éditer"><i class="bi bi-pencil"></i></button>
|
||||
<StartStopLiveSurvey
|
||||
{survey}
|
||||
class="ms-1 float-end"
|
||||
on:update={() => goto(`surveys/${survey.id}/admin`)}
|
||||
/>
|
||||
<button
|
||||
class="btn btn-outline-dark ms-1 float-end"
|
||||
title="Partager les résultats"
|
||||
on:click={() => shareResults(survey)}
|
||||
><i class="bi bi-share-fill"></i></button>
|
||||
{#if $user && $user.is_admin}
|
||||
<button class="btn btn-primary ms-1 float-end" on:click={() => { edit = !edit; } } title="Éditer"><i class="bi bi-pencil"></i></button>
|
||||
<StartStopLiveSurvey
|
||||
{survey}
|
||||
class="ms-1 float-end"
|
||||
on:update={() => goto(`surveys/${survey.id}/admin`)}
|
||||
/>
|
||||
<button
|
||||
class="btn btn-outline-dark ms-1 float-end"
|
||||
title="Partager les résultats"
|
||||
on:click={() => shareResults(survey)}
|
||||
><i class="bi bi-share-fill"></i></button>
|
||||
<button
|
||||
class="btn ms-1 float-end"
|
||||
class:btn-dark={exportview}
|
||||
class:btn-outline-dark={!exportview}
|
||||
title="Afficher les graphiques"
|
||||
on:click={() => { exportview = !exportview; } }
|
||||
><i class="bi bi-activity"></i></button>
|
||||
{#if exportview}
|
||||
<button
|
||||
class="btn ms-1 float-end"
|
||||
class:btn-dark={exportview}
|
||||
class:btn-outline-dark={!exportview}
|
||||
title="Afficher les graphiques"
|
||||
on:click={() => { exportview = !exportview; } }
|
||||
><i class="bi bi-activity"></i></button>
|
||||
{#if exportview}
|
||||
<button
|
||||
class="btn ms-1 float-end"
|
||||
class:btn-dark={exportview_list}
|
||||
class:btn-outline-dark={!exportview_list}
|
||||
title="Traiter les listes comme des textes : ne pas chercher à les regrouper sous un diagramme"
|
||||
on:click={() => { exportview_list = !exportview_list; } }
|
||||
><i class="bi bi-view-list"></i></button>
|
||||
{/if}
|
||||
class:btn-dark={exportview_list}
|
||||
class:btn-outline-dark={!exportview_list}
|
||||
title="Traiter les listes comme des textes : ne pas chercher à les regrouper sous un diagramme"
|
||||
on:click={() => { exportview_list = !exportview_list; } }
|
||||
><i class="bi bi-view-list"></i></button>
|
||||
{/if}
|
||||
<div class="d-flex align-items-center">
|
||||
<h2>
|
||||
<a href="surveys/{survey.id}" class="text-muted" style="text-decoration: none"><</a>
|
||||
{survey.title}
|
||||
<small class="text-muted">{#if exportview}Réponses{:else}Corrections{/if}</small>
|
||||
</h2>
|
||||
<SurveyBadge class="ms-2" {survey} />
|
||||
{/if}
|
||||
<div class="d-flex align-items-center">
|
||||
<h2>
|
||||
<a href="surveys/{survey.id}" class="text-muted" style="text-decoration: none"><</a>
|
||||
{survey.title}
|
||||
<small class="text-muted">{#if exportview}Réponses{:else}Corrections{/if}</small>
|
||||
</h2>
|
||||
<SurveyBadge class="ms-2" {survey} />
|
||||
</div>
|
||||
|
||||
{#if $user && $user.is_admin && edit}
|
||||
<SurveyAdmin {survey} on:saved={() => edit = false} />
|
||||
{/if}
|
||||
|
||||
{#await getQuestions(survey.id)}
|
||||
<div class="text-center">
|
||||
<div class="spinner-border text-primary mx-3" role="status"></div>
|
||||
<span>Chargement des questions …</span>
|
||||
</div>
|
||||
|
||||
{#if $user && $user.is_admin && edit}
|
||||
<SurveyAdmin {survey} on:saved={() => edit = false} />
|
||||
{/if}
|
||||
|
||||
{#await getQuestions(survey.id)}
|
||||
<div class="text-center">
|
||||
<div class="spinner-border text-primary mx-3" role="status"></div>
|
||||
<span>Chargement des questions …</span>
|
||||
</div>
|
||||
{:then questions}
|
||||
{#if !exportview}
|
||||
<div class="card mt-3 mb-5">
|
||||
<table class="table table-hover table-striped mb-0">
|
||||
<thead>
|
||||
{:then questions}
|
||||
{#if !exportview}
|
||||
<div class="card mt-3 mb-5">
|
||||
<table class="table table-hover table-striped mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Question</th>
|
||||
<th>Réponses</th>
|
||||
<th>Moyenne</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody ng-controller="SurveyGradesController">
|
||||
{#each questions as question (question.id)}
|
||||
<tr>
|
||||
<th>Question</th>
|
||||
<th>Réponses</th>
|
||||
<th>Moyenne</th>
|
||||
<td><a href="surveys/{survey.id}/responses/{question.id}">{question.title}</a></td>
|
||||
{#await question.getResponses()}
|
||||
<td colspan="2" class="text-center">
|
||||
<div class="spinner-border mx-3" role="status"></div>
|
||||
<span>Chargement …</span>
|
||||
</td>
|
||||
{:then responses}
|
||||
<td>
|
||||
{#if responses}
|
||||
{responses.filter((r) => !r.time_scored || (r.time_reported && r.time_reported >= r.time_scored)).length} /
|
||||
{responses.length}
|
||||
{#await usersP then users}
|
||||
<br>
|
||||
{Math.trunc(responses.length/users.length*1000)/10} %
|
||||
{/await}
|
||||
{:else}
|
||||
0
|
||||
{/if}
|
||||
</td>
|
||||
<td>
|
||||
{#if responses && responses.filter((r) => r.time_scored).length}
|
||||
{Math.trunc(responses.reduce((p, c) => (p + (c.score?c.score:0)), 0)/responses.filter((r) => r.time_scored).length*10)/10} %
|
||||
{:else}
|
||||
-- %
|
||||
{/if}
|
||||
</td>
|
||||
{/await}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody ng-controller="SurveyGradesController">
|
||||
{#each questions as question (question.id)}
|
||||
<tr>
|
||||
<td><a href="surveys/{survey.id}/responses/{question.id}">{question.title}</a></td>
|
||||
{#await question.getResponses()}
|
||||
<td colspan="2" class="text-center">
|
||||
<div class="spinner-border mx-3" role="status"></div>
|
||||
<span>Chargement …</span>
|
||||
</td>
|
||||
{:then responses}
|
||||
<td>
|
||||
{#if responses}
|
||||
{responses.filter((r) => !r.time_scored || (r.time_reported && r.time_reported >= r.time_scored)).length} /
|
||||
{responses.length}
|
||||
{#await usersP then users}
|
||||
<br>
|
||||
{Math.trunc(responses.length/users.length*1000)/10} %
|
||||
{/await}
|
||||
{:else}
|
||||
0
|
||||
{/if}
|
||||
</td>
|
||||
<td>
|
||||
{#if responses && responses.filter((r) => r.time_scored).length}
|
||||
{Math.trunc(responses.reduce((p, c) => (p + (c.score?c.score:0)), 0)/responses.filter((r) => r.time_scored).length*10)/10} %
|
||||
{:else}
|
||||
-- %
|
||||
{/if}
|
||||
</td>
|
||||
{/await}
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{:else}
|
||||
{#each questions as question (question.id)}
|
||||
<h3>{question.title}</h3>
|
||||
{#if question.kind == "text" || (exportview_list && question.kind.indexOf("list") == 0)}
|
||||
{#await question.getResponses() then responses}
|
||||
{#each responses as response (response.id)}
|
||||
<div class="card mb-2">
|
||||
<div class="card-body">
|
||||
<p class="card-text" style:white-space="pre-line">
|
||||
{response.value}
|
||||
</p>
|
||||
</div>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{:else}
|
||||
{#each questions as question (question.id)}
|
||||
<h3>{question.title}</h3>
|
||||
{#if question.kind == "text" || (exportview_list && question.kind.indexOf("list") == 0)}
|
||||
{#await question.getResponses() then responses}
|
||||
{#each responses as response (response.id)}
|
||||
<div class="card mb-2">
|
||||
<div class="card-body">
|
||||
<p class="card-text" style:white-space="pre-line">
|
||||
{response.value}
|
||||
</p>
|
||||
</div>
|
||||
{/each}
|
||||
{/await}
|
||||
{:else}
|
||||
<CorrectionPieChart {question} />
|
||||
{/if}
|
||||
<hr class="mb-3">
|
||||
{/each}
|
||||
{/if}
|
||||
{/await}
|
||||
</div>
|
||||
{/each}
|
||||
{/await}
|
||||
{:else}
|
||||
<CorrectionPieChart {question} />
|
||||
{/if}
|
||||
<hr class="mb-3">
|
||||
{/each}
|
||||
{/if}
|
||||
{/await}
|
||||
|
||||
<div class="modal fade" tabindex="-1" id="shareModal">
|
||||
|
|
|
|||
8
ui/src/routes/surveys/[sid]/responses/[rid]/+page.js
Normal file
8
ui/src/routes/surveys/[sid]/responses/[rid]/+page.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
export async function load({ params, parent }) {
|
||||
const stuff = await parent();
|
||||
|
||||
return {
|
||||
survey: stuff.survey,
|
||||
rid: params.rid,
|
||||
};
|
||||
}
|
||||
|
|
@ -1,16 +1,3 @@
|
|||
<script context="module">
|
||||
import { getSurvey } from '$lib/surveys';
|
||||
|
||||
export async function load({ params, stuff }) {
|
||||
return {
|
||||
props: {
|
||||
surveyP: stuff.survey,
|
||||
rid: params.rid,
|
||||
},
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import Correction from '$lib/components/Correction.svelte';
|
||||
import CorrectionPieChart from '$lib/components/CorrectionPieChart.svelte';
|
||||
|
|
@ -20,8 +7,7 @@
|
|||
import { getCorrectionTemplates } from '$lib/correctionTemplates';
|
||||
import { getQuestion } from '$lib/questions';
|
||||
|
||||
export let surveyP;
|
||||
export let rid;
|
||||
export let data;
|
||||
|
||||
let showChart = false;
|
||||
let showResponses = false;
|
||||
|
|
@ -31,152 +17,156 @@
|
|||
|
||||
let child;
|
||||
let waitApply = false;
|
||||
let ctpls = getCorrectionTemplates(rid);
|
||||
let filter = "";
|
||||
|
||||
let ctpls;
|
||||
let cts = { };
|
||||
ctpls.then((ctpls) => {
|
||||
for (const tpl of ctpls) {
|
||||
cts[tpl.id] = { };
|
||||
tpl.getCorrections().then((c) => {
|
||||
if (c) {
|
||||
for (const d of c) {
|
||||
cts[tpl.id][d.id_user] = d;
|
||||
}
|
||||
$: {
|
||||
ctpls = getCorrectionTemplates(data.rid);
|
||||
if (ctpls) {
|
||||
ctpls.then((ctpls) => {
|
||||
for (const tpl of ctpls) {
|
||||
cts[tpl.id] = { };
|
||||
tpl.getCorrections().then((c) => {
|
||||
if (c) {
|
||||
for (const d of c) {
|
||||
cts[tpl.id][d.id_user] = d;
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
cts = cts;
|
||||
});
|
||||
}
|
||||
let filter = "";
|
||||
|
||||
let nodescription = false;
|
||||
let y = 0;
|
||||
$: nodescription = y > 10;
|
||||
|
||||
let survey = null;
|
||||
$: survey = data.survey;
|
||||
</script>
|
||||
|
||||
<svelte:window bind:scrollY={y} />
|
||||
|
||||
{#await surveyP then survey}
|
||||
{#await getQuestion(rid)}
|
||||
{#await getQuestion(data.rid)}
|
||||
<div class="text-center">
|
||||
<div class="spinner-border text-primary mx-3" role="status"></div>
|
||||
<span>Chargement de la question…</span>
|
||||
</div>
|
||||
{:then question}
|
||||
{#await ctpls}
|
||||
<div class="text-center">
|
||||
<div class="spinner-border text-primary mx-3" role="status"></div>
|
||||
<span>Chargement de la question…</span>
|
||||
<span>Chargement des templates…</span>
|
||||
</div>
|
||||
{:then correctionTemplates}
|
||||
<div class="float-end">
|
||||
<input
|
||||
class="form-control"
|
||||
placeholder="filtre"
|
||||
bind:value={filter}
|
||||
>
|
||||
</div>
|
||||
<div class="d-flex align-items-center">
|
||||
<h2>
|
||||
<a href="surveys/{survey.id}/responses" class="text-muted" style="text-decoration: none"><</a>
|
||||
{survey.title}
|
||||
<small class="text-muted">Corrections</small>
|
||||
</h2>
|
||||
<SurveyBadge class="ms-2" {survey} />
|
||||
</div>
|
||||
{:then question}
|
||||
{#await ctpls}
|
||||
<div class="text-center">
|
||||
<div class="spinner-border text-primary mx-3" role="status"></div>
|
||||
<span>Chargement de la question…</span>
|
||||
</div>
|
||||
{:then correctionTemplates}
|
||||
<div class="float-end">
|
||||
<input
|
||||
class="form-control"
|
||||
placeholder="filtre"
|
||||
bind:value={filter}
|
||||
>
|
||||
</div>
|
||||
<div class="d-flex align-items-center">
|
||||
<h2>
|
||||
<a href="surveys/{survey.id}/responses" class="text-muted" style="text-decoration: none"><</a>
|
||||
{survey.title}
|
||||
<small class="text-muted">Corrections</small>
|
||||
</h2>
|
||||
<SurveyBadge class="ms-2" {survey} />
|
||||
</div>
|
||||
|
||||
<div class="card sticky-top" style="overflow-y: auto; max-height: 70vh">
|
||||
<QuestionHeader
|
||||
{question}
|
||||
nodescription={nodescription}
|
||||
>
|
||||
<button
|
||||
class="btn btn-sm btn-link float-start"
|
||||
on:click={() => showResponses = !showResponses}
|
||||
>
|
||||
<i
|
||||
class="bi"
|
||||
class:bi-chevron-right={!showResponses}
|
||||
class:bi-chevron-down={showResponses}
|
||||
></i>
|
||||
</button>
|
||||
{#if showResponses}
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-success float-end ms-3 me-1"
|
||||
title="Appliquer les corrections par regexp"
|
||||
on:click={() => {waitApply = true; child.applyCorrections().then(() => { waitApply = false; })} }
|
||||
disabled={waitApply}
|
||||
>
|
||||
{#if waitApply}
|
||||
<div class="spinner-border spinner-border-sm" role="status"></div>
|
||||
{:else}
|
||||
<i class="bi bi-check-all"></i>
|
||||
{/if}
|
||||
</button>
|
||||
{/if}
|
||||
{#if question.kind == "ucq" || question.kind == "mcq" || question.kind == "int"}
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm float-end mx-1"
|
||||
class:btn-outline-info={!showChart}
|
||||
class:btn-info={showChart}
|
||||
on:click={() => showChart = !showChart}
|
||||
title="Afficher les résultats"
|
||||
>
|
||||
<i class="bi bi-bar-chart-line-fill"></i>
|
||||
</button>
|
||||
{/if}
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm float-end mx-1"
|
||||
class:btn-outline-info={!showStudent}
|
||||
class:btn-info={showStudent}
|
||||
on:click={() => showStudent = !showStudent}
|
||||
title="Afficher les étudiants"
|
||||
>
|
||||
<i class="bi bi-people"></i>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm float-end mx-1"
|
||||
class:btn-outline-info={!notCorrected}
|
||||
class:btn-info={notCorrected}
|
||||
on:click={() => notCorrected = !notCorrected}
|
||||
title="Afficher les réponses corrigées"
|
||||
>
|
||||
<i class="bi bi-files"></i>
|
||||
</button>
|
||||
</QuestionHeader>
|
||||
{#if showResponses}
|
||||
<CorrectionReference
|
||||
class="card-body"
|
||||
{cts}
|
||||
bind:filter={filter}
|
||||
{nb_responses}
|
||||
{question}
|
||||
templates={correctionTemplates}
|
||||
/>
|
||||
{/if}
|
||||
{#if showChart}
|
||||
<CorrectionPieChart
|
||||
{question}
|
||||
class="card-body"
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<Correction
|
||||
{cts}
|
||||
{filter}
|
||||
<div class="card sticky-top" style="overflow-y: auto; max-height: 70vh">
|
||||
<QuestionHeader
|
||||
{question}
|
||||
{showStudent}
|
||||
{notCorrected}
|
||||
bind:child={child}
|
||||
templates={correctionTemplates}
|
||||
on:nb_responses={(v) => { nb_responses = v.detail; } }
|
||||
/>
|
||||
{/await}
|
||||
nodescription={nodescription}
|
||||
>
|
||||
<button
|
||||
class="btn btn-sm btn-link float-start"
|
||||
on:click={() => showResponses = !showResponses}
|
||||
>
|
||||
<i
|
||||
class="bi"
|
||||
class:bi-chevron-right={!showResponses}
|
||||
class:bi-chevron-down={showResponses}
|
||||
></i>
|
||||
</button>
|
||||
{#if showResponses}
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-success float-end ms-3 me-1"
|
||||
title="Appliquer les corrections par regexp"
|
||||
on:click={() => {waitApply = true; child.applyCorrections().then(() => { waitApply = false; })} }
|
||||
disabled={waitApply}
|
||||
>
|
||||
{#if waitApply}
|
||||
<div class="spinner-border spinner-border-sm" role="status"></div>
|
||||
{:else}
|
||||
<i class="bi bi-check-all"></i>
|
||||
{/if}
|
||||
</button>
|
||||
{/if}
|
||||
{#if question.kind == "ucq" || question.kind == "mcq" || question.kind == "int"}
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm float-end mx-1"
|
||||
class:btn-outline-info={!showChart}
|
||||
class:btn-info={showChart}
|
||||
on:click={() => showChart = !showChart}
|
||||
title="Afficher les résultats"
|
||||
>
|
||||
<i class="bi bi-bar-chart-line-fill"></i>
|
||||
</button>
|
||||
{/if}
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm float-end mx-1"
|
||||
class:btn-outline-info={!showStudent}
|
||||
class:btn-info={showStudent}
|
||||
on:click={() => showStudent = !showStudent}
|
||||
title="Afficher les étudiants"
|
||||
>
|
||||
<i class="bi bi-people"></i>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm float-end mx-1"
|
||||
class:btn-outline-info={!notCorrected}
|
||||
class:btn-info={notCorrected}
|
||||
on:click={() => notCorrected = !notCorrected}
|
||||
title="Afficher les réponses corrigées"
|
||||
>
|
||||
<i class="bi bi-files"></i>
|
||||
</button>
|
||||
</QuestionHeader>
|
||||
{#if showResponses}
|
||||
<CorrectionReference
|
||||
class="card-body"
|
||||
{cts}
|
||||
bind:filter={filter}
|
||||
{nb_responses}
|
||||
{question}
|
||||
templates={correctionTemplates}
|
||||
/>
|
||||
{/if}
|
||||
{#if showChart}
|
||||
<CorrectionPieChart
|
||||
{question}
|
||||
class="card-body"
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<Correction
|
||||
{cts}
|
||||
{filter}
|
||||
{question}
|
||||
{showStudent}
|
||||
{notCorrected}
|
||||
bind:child={child}
|
||||
templates={correctionTemplates}
|
||||
on:nb_responses={(v) => { nb_responses = v.detail; } }
|
||||
/>
|
||||
{/await}
|
||||
{/await}
|
||||
|
||||
|
|
|
|||
Reference in a new issue