ui: Working flags

This commit is contained in:
nemunaire 2021-08-31 02:54:59 +02:00
parent ef899ee99b
commit 1def2c97c1
8 changed files with 340 additions and 15 deletions

View File

@ -8,17 +8,88 @@
Icon,
ListGroup,
ListGroupItem,
Spinner,
} from 'sveltestrap';
export let exercice = {};
import FlagKey from './FlagKey.svelte';
import FlagMCQ from './FlagMCQ.svelte';
export let exercice = { };
export let flags = [];
function submitFlags(event) {
console.log(event);
export let refresh_my;
export let refresh_teams;
function waitDiff(i) {
refresh_my((my) => {
if (my && my.exercices[exercice.id].tries != exercice.tries) {
submitInProgress = false;
refresh_teams();
} else if (i > 0) {
setTimeout(waitDiff, 450, i-1);
}
})
}
let responses = { };
async function submitFlags() {
submitInProgress = true;
sberr = "";
message = "";
const response = await fetch(
"/submit/" + exercice.id,
{
method: "POST",
body: JSON.stringify(responses),
}
)
if (response.status < 300) {
const data = await response.json();
messageClass = 'text-success';
message = data.errmsg;
waitDiff(20);
} else {
submitInProgress = false;
messageClass = 'text-danger';
let data = "";
try {
data = await response.json();
} catch(e) {
data = null;
}
if (data && data.errmsg)
message = data.errmsg;
if (response.statys != 402)
sberr = "Oups !";
}
}
function resetResponses() {
responses = {
flags: { },
mcqs: { },
justifications: { },
};
}
let last_exercice = null;
$: {
if (!last_exercice || last_exercice != exercice.id) {
last_exercice = exercice.id;
resetResponses()
}
}
let sberr = "";
let message = "";
let messageClass = "text-danger";
let timeouted = false;
let submitInProgress = false;
</script>
<Card class="border-danger mb-2">
@ -40,7 +111,7 @@
</ListGroupItem>
{/if}
{#if exercice.submitted || sberr}
<ListGroupItem>
<ListGroupItem class="{messageClass}">
{#if !sberr}
<strong>Votre solution a bien été envoyée !</strong>
{:else}
@ -48,7 +119,7 @@
{/if}
</ListGroupItem>
{/if}
{#if exercice.timeouted}
{#if timeouted}
<ListGroupItem class="text-danger">
<strong>Oops</strong>
La requête a dépassé le délai d'attente. Vous devriez réessayer dans quelques instant&hellip;
@ -59,12 +130,34 @@
{#if !exercice.submitted || sberr}
<CardBody>
<form on:submit|preventDefault={submitFlags}>
{JSON.stringify(flags)}
{#each flags as flag ((flag.type?flag.type:"i") + flag.id)}
{#if flag.type == "mcq"}
<FlagMCQ
exercice_id={exercice.id}
{flag}
{refresh_my}
bind:values={responses.mcqs}
bind:justifications={responses.justifications}
/>
{:else}
<FlagKey
exercice_id={exercice.id}
{flag}
{refresh_my}
bind:value={responses.flags[flag.id]}
/>
{/if}
{/each}
<div class="form-group mt-2">
<Button
type="submit"
color="danger"
disabled={submitInProgress}
>
{#if submitInProgress}
<Spinner size="sm" class="me-2" />
{/if}
Soumettre
</Button>
</div>

View File

@ -0,0 +1,173 @@
<script>
import {
Button,
Icon,
Spinner,
} from 'sveltestrap';
import { settings } from '../stores/settings.js';
export let refresh_my = null;
export let exercice_id = 0;
export let flag = { };
export let value = "";
let values = [""];
function waitChoices(i) {
refresh_my((my) => {
let haveChoices = false;
if (my && my.exercices[exercice_id].flags) {
my.exercices[exercice_id].flags.forEach((f) => {
if (f.id == flag.id && f.choices) {
haveChoices = true;
}
})
}
if (haveChoices) {
wcsubmitted = false;
} else if (i > 0) {
setTimeout(waitChoices, 450, i-1);
}
})
}
let wcsubmitted = false;
async function wantchoices() {
wcsubmitted = true;
const response = await fetch(
"/wantchoices/" + exercice_id,
{
method: "POST",
body: JSON.stringify({ id: Number(flag.id) }),
}
)
if (response.status < 300) {
waitChoices(15);
} else {
wcsubmitted = false;
}
}
function addItem() {
values.push("");
values = values;
}
$: {
let v = values.slice();
// Remove empty cells
if (!flag.nb_lines) {
for (let i = v.length - 1; i > 0; i--) {
if (!v[i].length) {
v.splice(i, 1);
}
}
}
// Sort cells
if (flag.ignore_order) {
v = v.sort();
}
value = v.join(flag.separator ? flag.separator : ',');
if (flag.separator) {
value += flag.separator;
}
}
$: {
if (flag.nb_lines) {
while (values.length != flag.nb_lines) {
if (values.length > flag.nb_lines) {
values.pop();
} else {
values.push("");
}
}
}
}
</script>
<div class="form-group mb-3">
<label for="sol_{flag.type}{flag.id}_0">{flag.label}&nbsp;:</label>
{#if flag.found && flag.value}
<span>{flag.value}</span>
{/if}
{#if !flag.found}
{#each values as v, index}
<div class="input-group" class:mt-1={index != 0}>
{#if !flag.choices}
{#if !flag.multiline}
<input
type="text"
class="form-control flag"
id="sol_{flag.type}{flag.id}_{index}"
autocomplete="off"
bind:value={values[index]}
placeholder={flag.placeholder}
title={flag.placeholder}
>
{:else}
<textarea
class="form-control flag"
id="sol_{flag.type}{flag.id}_{index}"
autocomplete="off"
bind:value={values[index]}
placeholder="{flag.placeholder}"
title="{flag.placeholder}"
></textarea>
{/if}
{:else}
<select
class="form-select"
id="sol_{flag.type}{flag.id}_{index}"
bind:value={values[index]}
>
{#each Object.keys(flag.choices) as l}
<option value={l}>{flag.choices[l]}</option>
{/each}
</select>
{/if}
{#if flag.choices_cost > 0}
<Button
color="success"
type="button"
on:click={wantchoices}
disabled={wcsubmitted}
title="Cliquez pour échanger ce champ de texte par une liste de choix. L'opération vous coûtera {flag.choices_cost * $settings.wchoiceCurrentCoefficient} points."
>
{#if wcsubmitted}
<Spinner size="sm" class="me-2" />
{/if}
<Icon name="tasks" />
Liste de propositions ({flag.choices_cost * $settings.wchoiceCurrentCoefficient} {flag.choices_cost * $settings.wchoiceCurrentCoefficient===1?"point":"points"})
</Button>
{:else if flag.separator && !flag.nb_lines && index == values.length - 1}
<Button
color="success"
type="button"
title="Ajouter un élément."
on:click={addItem}
>
<Icon name="plus" />
</Button>
{/if}
</div>
{/each}
{#if flag.help}
<small class="form-text text-muted">{flag.help}</small>
{/if}
{:else}
<Icon
name="check"
class="form-control-feedback text-success"
aria-hidden="true"
title="Flag trouvé à {flag.found}"
/>
{/if}
</div>

View File

@ -0,0 +1,49 @@
<script>
import {
Button,
Icon,
} from 'sveltestrap';
import FlagKey from './FlagKey.svelte';
export let refresh_my = null;
export let exercice_id = 0;
export let flag = { };
export let values = { };
export let justifications = { };
</script>
{#if flag.label}
<p class="mb-1">
{flag.label}&nbsp;:
{#if flag.found}
<Icon name="check" class="form-control-feedback text-success" aria-hidden="true" title="QCM réussi à {flag.solved}" />
{/if}
</p>
{/if}
{#if !flag.found || flag.justify}
{#each Object.keys(flag.choices) as cid, index}
<div class="form-check ms-3">
<input class="form-check-input" type="checkbox" id="mcq_{flag.id}_{cid}" bind:checked={values[Number(cid)]} disabled={flag.found}>
<label class="form-check-label" for="mcq_{flag.id}_{cid}">
{#if typeof flag.choices[cid] == "Object"}
{flag.choices[cid].label}
{:else}
{flag.choices[cid]}
{/if}
</label>
{#if values[Number(cid)] && flag.justify && (!flag.choices[cid].justification || !flag.choices[cid].justification.solved)}
<FlagKey
{exercice_id}
flag={flag.choices[cid].justification}
{refresh_my}
bind:values={justifications[flag.choices[cid].justification.id]}
/>
{/if}
{#if flag.choices[cid].justification && flag.choices[cid].justification.solved}
<Icon name="check" class="form-control-feedback text-success" aria-hidden="true" title="Flag trouvé !" />
{/if}
</div>
{/each}
{/if}
<hr>

View File

@ -20,7 +20,7 @@
messageClass = "info";
sberr = "Votre nom d'équipe a été changé avec succès.";
message = "";
} else {
} else if (i > 0) {
setTimeout(gotoHomeOnDiff, 850, i-1);
}
})

View File

@ -18,6 +18,8 @@
props: {
theme: context.theme,
exercice: exercice,
refresh_my: context.refresh_my,
refresh_teams: context.refresh_teams,
}
};
}
@ -45,6 +47,9 @@
export let theme;
export let exercice;
export let refresh_my;
export let refresh_teams;
</script>
<svelte:head>
@ -147,8 +152,13 @@
</Col>
{/if}
<Col lg class="mb-5">
{#if !$my.exercices[exercice.id].solved}
<ExerciceFlags {exercice} flags={$my.exercices[exercice.id].flags} />
{#if !$my.exercices[exercice.id].solved_rank}
<ExerciceFlags
{refresh_my}
{refresh_teams}
exercice={$my.exercices[exercice.id]}
flags={$my.exercices[exercice.id].flags}
/>
{:else}
<ExerciceSolved theme={theme} exercice={$my.exercices[exercice.id]} />
{/if}

View File

@ -38,19 +38,19 @@
on:click={goto(`/${theme.urlid}/${theme.exercices[k].urlid}`)}
>
<div class="row">
<div class="col-1" style="margin-top: -0.5rem; margin-bottom: -0.5rem; text-align: right; border-right: 5px solid #{$my && $my.exercices[k] && $my.exercices[k].solved ? '62c462' : 'aaa'}">
<div class="col-1" style="margin-top: -0.5rem; margin-bottom: -0.5rem; text-align: right; border-right: 5px solid #{$my && $my.exercices[k] && $my.exercices[k].solved_rank ? '62c462' : 'aaa'}">
</div>
<div class="col-10">
<div style="position: absolute; margin-left: calc(var(--bs-gutter-x) * -.5 - 15px); margin-top: -0.5rem;">
<svg style="height: 50px; width: 23px;">
<rect
style="fill:#{$my && $my.exercices[k] && (index < 1 || $my.exercices[Object.keys(theme.exercices)[index-1]].solved) ? '62c462' : 'aaa'}"
style="fill:#{$my && $my.exercices[k] && (index < 1 || $my.exercices[Object.keys(theme.exercices)[index-1]].solved_rank) ? '62c462' : 'aaa'}"
width="5"
height="30"
x="10"
y="0" />
<path
style="fill:#{$my && $my.exercices[k] ? ($my.exercices[k].solved ? '62c462' : (theme.exercices[k].curcoeff > 1.0 ? 'f89406' : '5bc0de')) : '555'}"
style="fill:#{$my && $my.exercices[k] ? ($my.exercices[k].solved_rank ? '62c462' : (theme.exercices[k].curcoeff > 1.0 ? 'f89406' : '5bc0de')) : '555'}"
d="m 22,20 a 9.5700617,9.5700617 0 0 1 -9.5690181,9.57006 9.5700617,9.5700617 0 0 1 -9.57110534,-9.56797 9.5700617,9.5700617 0 0 1 9.56692984,-9.57215 9.5700617,9.5700617 0 0 1 9.5731926,9.56588" />
</svg>
</div>

View File

@ -39,7 +39,7 @@
refresh_my((my) => {
if (my && my.team_id) {
goto('/');
} else {
} else if (i > 0) {
setTimeout(gotoHomeOnDiff, 650, i-1);
}
})

View File

@ -11,7 +11,7 @@ export const myThemes = derived([my, themesStore], ([$my, $themesStore]) => {
if ($my && $my.exercices) {
for (let k in $themesStore.themes[key].exercices) {
if ($my.exercices[k] && $my.exercices[k].solved) {
if ($my.exercices[k] && $my.exercices[k].solved_rank) {
themes[key].exercice_solved++;
}
}
@ -32,7 +32,7 @@ export const tags = derived([my, themesStore], ([$my, $themesStore]) => {
else
tags[tag].count += 1;
if ($my && $my.exercices && $my.exercices[k] && $my.exercices[k].solved)
if ($my && $my.exercices && $my.exercices[k] && $my.exercices[k].solved_rank)
tags[tag].solved += 1;
});
}