ui: Working flags
This commit is contained in:
parent
ef899ee99b
commit
1def2c97c1
@ -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…
|
||||
@ -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>
|
||||
|
173
frontend/ui/src/components/FlagKey.svelte
Normal file
173
frontend/ui/src/components/FlagKey.svelte
Normal 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} :</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>
|
49
frontend/ui/src/components/FlagMCQ.svelte
Normal file
49
frontend/ui/src/components/FlagMCQ.svelte
Normal 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} :
|
||||
{#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>
|
@ -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);
|
||||
}
|
||||
})
|
||||
|
@ -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}
|
||||
|
@ -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>
|
||||
|
@ -39,7 +39,7 @@
|
||||
refresh_my((my) => {
|
||||
if (my && my.team_id) {
|
||||
goto('/');
|
||||
} else {
|
||||
} else if (i > 0) {
|
||||
setTimeout(gotoHomeOnDiff, 650, i-1);
|
||||
}
|
||||
})
|
||||
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user