Skip to content
GitLab
Explorer
Connexion
S'inscrire
Navigation principale
Rechercher ou aller à…
Projet
LanguageLab
Gestion
Activité
Membres
Labels
Programmation
Tickets
Tableaux des tickets
Jalons
Wiki
Code
Requêtes de fusion
Dépôt
Branches
Validations
Étiquettes
Graphe du dépôt
Comparer les révisions
Extraits de code
Déploiement
Releases
Registre de paquets
Registre de conteneur
Registre de modèles
Opération
Modules Terraform
Surveillance
Incidents
Analyse
Données d'analyse des chaînes de valeur
Analyse des contributeurs
Données d'analyse du dépôt
Expériences du modèle
Aide
Aide
Support
Documentation de GitLab
Comparer les forfaits GitLab
Forum de la communauté
Contribuer à GitLab
Donner votre avis
Conditions générales et politique de confidentialité
Raccourcis clavier
?
Extraits de code
Groupes
Projets
Afficher davantage de fils d'Ariane
Serge Bibauw
LanguageLab
Requêtes de fusion
!38
New studies & tests logic
Code
Examiner les modifications
Extraire la branche
Télécharger
Correctifs
Diff brut
Fusionnées
New studies & tests logic
surveys
vers
dev
Vue d'ensemble
0
Validations
44
Pipelines
0
Modifications
4
Fusionnées
Brieuc Dubois
a demandé de fusionner
surveys
vers
dev
Il y a 3 mois
Vue d'ensemble
0
Validations
44
Pipelines
0
Modifications
4
Étendre
0
0
Rapports de requête de fusion
Affichage du commit
b0b6d3bd
Précédent
Suivant
Afficher la dernière version
4 fichiers
+
0
−
499
En ligne
Comparer les modifications
Côte à côte
En ligne
Afficher les modifications des espaces
Afficher un fichier à la fois
Fichiers
4
Rechercher (par ex. *.vue) (Ctrl+P)
b0b6d3bd
Remove old tests
· b0b6d3bd
Brieuc Dubois
rédigé
Il y a 3 mois
frontend/src/routes/tests/[id]/+page.svelte supprimé
100644 → 0
+
0
−
460
Options
<script
lang=
"ts"
>
import
{
sendSurveyResponseAPI
,
sendSurveyResponseInfoAPI
}
from
'
$lib/api/survey
'
;
import
{
getSurveyScoreAPI
}
from
'
$lib/api/survey
'
;
import
{
t
}
from
'
$lib/services/i18n
'
;
import
{
toastWarning
}
from
'
$lib/utils/toasts.js
'
;
import
{
get
}
from
'
svelte/store
'
;
import
type
SurveyGroup
from
'
$lib/types/surveyGroup
'
;
import
Gapfill
from
'
$lib/components/surveys/gapfill.svelte
'
;
import
type
{
PageData
}
from
'
./$types
'
;
import
Consent
from
'
$lib/components/surveys/consent.svelte
'
;
import
Dropdown
from
'
$lib/components/surveys/dropdown.svelte
'
;
import
config
from
'
$lib/config
'
;
import
type
User
from
'
$lib/types/user
'
;
import
type
Survey
from
'
$lib/types/survey
'
;
let
{
data
}:
{
data
:
PageData
}
=
$props
();
let
{
user
,
survey
}:
{
user
:
User
|
null
;
survey
:
Survey
}
=
data
;
let
startTime
=
new
Date
().
getTime
();
function
getSortedQuestions
(
group
:
SurveyGroup
)
{
return
group
.
questions
.
sort
(()
=>
Math
.
random
()
-
0.5
);
}
let
step
=
$state
(
user
?
2
:
0
);
let
uid
=
$state
(
user
?.
id
||
null
);
let
code
=
$state
(
''
);
let
subStep
=
$state
(
0
);
let
currentGroupId
=
$state
(
0
);
survey
.
groups
.
sort
((
a
,
b
)
=>
{
if
(
a
.
demo
===
b
.
demo
)
{
return
0
;
}
return
a
.
demo
?
-
1
:
1
;
});
let
currentGroup
=
$derived
(
survey
.
groups
[
currentGroupId
]);
let
questionsRandomized
=
$derived
(
getSortedQuestions
(
currentGroup
));
let
currentQuestionId
=
$state
(
0
);
let
currentQuestion
=
$derived
(
questionsRandomized
[
currentQuestionId
]);
let
type
=
$derived
(
currentQuestion
?.
question
.
split
(
'
:
'
)[
0
]);
let
value
=
$derived
(
currentQuestion
?.
question
.
split
(
'
:
'
).
slice
(
1
).
join
(
'
:
'
));
let
gaps
=
$derived
(
type
===
'
gap
'
?
gapParts
(
currentQuestion
.
question
)
:
null
);
let
soundPlayer
:
HTMLAudioElement
;
let
displayQuestionOptions
:
string
[]
=
$derived
(
(()
=>
{
let
d
=
[...(
currentQuestion
?.
options
??
[])];
shuffle
(
d
);
return
d
;
})()
);
let
finalScore
:
number
|
null
=
$state
(
null
);
let
selectedOption
:
string
;
let
endSurveyAnswers
:
{
[
key
:
string
]:
any
}
=
{};
//source: shuffle function code taken from https://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array/18650169#18650169
function
shuffle
(
array
:
string
[])
{
let
currentIndex
=
array
.
length
;
// While there remain elements to shuffle...
while
(
currentIndex
!=
0
)
{
// Pick a remaining element...
let
randomIndex
=
Math
.
floor
(
Math
.
random
()
*
currentIndex
);
currentIndex
--
;
// And swap it with the current element.
[
array
[
currentIndex
],
array
[
randomIndex
]]
=
[
array
[
randomIndex
],
array
[
currentIndex
]];
}
}
function
setGroupId
(
id
:
number
)
{
currentGroupId
=
id
;
if
(
currentGroup
.
id
<
1100
)
{
setQuestionId
(
0
);
}
}
function
setQuestionId
(
id
:
number
)
{
currentQuestionId
=
id
;
if
(
soundPlayer
)
soundPlayer
.
load
();
}
async
function
selectOption
(
option
:
string
)
{
if
(
!
currentGroup
.
demo
)
{
if
(
!
(
await
sendSurveyResponseAPI
(
fetch
,
code
,
sid
,
uid
,
survey
.
id
,
currentGroup
.
id
,
questionsRandomized
[
currentQuestionId
].
id
,
currentQuestion
.
options
.
findIndex
((
o
:
string
)
=>
o
===
option
)
+
1
,
(
new
Date
().
getTime
()
-
startTime
)
/
1000
))
)
{
return
;
}
}
if
(
currentQuestionId
<
questionsRandomized
.
length
-
1
)
{
setQuestionId
(
currentQuestionId
+
1
);
startTime
=
new
Date
().
getTime
();
}
else
{
nextGroup
();
}
}
async
function
sendGap
()
{
if
(
!
gaps
)
return
;
if
(
!
currentGroup
.
demo
)
{
const
gapTexts
=
gaps
.
filter
((
part
)
=>
part
.
gap
!==
null
)
.
map
((
part
)
=>
part
.
gap
)
.
join
(
'
|
'
);
if
(
!
(
await
sendSurveyResponseAPI
(
fetch
,
code
,
sid
,
uid
,
survey
.
id
,
currentGroup
.
id
,
questionsRandomized
[
currentQuestionId
].
id
,
-
1
,
(
new
Date
().
getTime
()
-
startTime
)
/
1000
,
gapTexts
))
)
{
return
;
}
}
if
(
currentQuestionId
<
questionsRandomized
.
length
-
1
)
{
setQuestionId
(
currentQuestionId
+
1
);
startTime
=
new
Date
().
getTime
();
}
else
{
nextGroup
();
}
}
async
function
nextGroup
()
{
if
(
currentGroupId
<
survey
.
groups
.
length
-
1
)
{
setGroupId
(
currentGroupId
+
1
);
//special group id for end of survey questions
if
(
currentGroup
.
id
>=
1100
)
{
const
scoreData
=
await
getSurveyScoreAPI
(
fetch
,
survey
.
id
,
sid
);
if
(
scoreData
)
{
finalScore
=
scoreData
.
score
;
}
step
+=
user
?
2
:
1
;
return
;
}
}
else
{
const
scoreData
=
await
getSurveyScoreAPI
(
fetch
,
survey
.
id
,
sid
);
if
(
scoreData
)
{
finalScore
=
scoreData
.
score
;
}
step
+=
2
;
}
}
function
checkCode
()
{
if
(
!
code
)
{
toastWarning
(
get
(
t
)(
'
surveys.invalidCode
'
));
return
;
}
if
(
code
.
length
<
3
)
{
toastWarning
(
get
(
t
)(
'
surveys.invalidCode
'
));
return
;
}
step
+=
1
;
}
function
gapParts
(
question
:
string
):
{
text
:
string
;
gap
:
string
|
null
}[]
{
if
(
!
question
.
startsWith
(
'
gap:
'
))
return
[];
const
gapText
=
question
.
split
(
'
:
'
).
slice
(
1
).
join
(
'
:
'
);
const
parts
:
{
text
:
string
;
gap
:
string
|
null
}[]
=
[];
for
(
let
part
of
gapText
.
split
(
/
(
<.+
?
>
)
/g
))
{
const
isGap
=
part
.
startsWith
(
'
<
'
)
&&
part
.
endsWith
(
'
>
'
);
const
text
=
isGap
?
part
.
slice
(
1
,
-
1
)
:
part
;
parts
.
push
({
text
:
text
,
gap
:
isGap
?
''
:
null
});
}
return
parts
;
}
async
function
selectAnswer
(
selection
:
string
,
option
:
string
)
{
endSurveyAnswers
[
selection
]
=
option
;
subStep
+=
1
;
if
(
subStep
==
5
)
{
await
sendSurveyResponseInfoAPI
(
fetch
,
survey
.
id
,
sid
,
endSurveyAnswers
.
birthYear
,
endSurveyAnswers
.
gender
,
endSurveyAnswers
.
primaryLanguage
,
endSurveyAnswers
.
other_language
,
endSurveyAnswers
.
education
);
step
+=
1
;
}
selectedOption
=
''
;
return
;
}
</script>
{
#if
step
==
0
}
<div
class=
"max-w-screen-md mx-auto p-20 flex flex-col items-center min-h-screen"
>
<h2
class=
"mb-10 text-xl text-center"
>
{
survey
.
title
}
</h2>
<p
class=
"mb-4 text-lg font-semibold"
>
{
$t
(
'
surveys.code
'
)
}
</p>
<p
class=
"mb-6 text-sm text-gray-600 text-center"
>
{
@html
$t
(
'
surveys.codeIndication
'
)
}
</p>
<input
type=
"text"
placeholder=
"Code"
class=
"input block mx-auto w-full max-w-xs border border-gray-300 rounded-md py-2 px-3 text-center"
onkeydown=
{
(
e
)
=>
e
.
key
===
'
Enter
'
&&
checkCode
()
}
bind:value=
{
code
}
/>
<button
class=
"button mt-4 block bg-yellow-500 text-white rounded-md py-2 px-6 hover:bg-yellow-600 transition"
onclick=
{
checkCode
}
>
{
$t
(
'
button.next
'
)
}
</button>
</div>
{
:else
if
step
==
1
}
<div
class=
"max-w-screen-md mx-auto p-5"
>
<Consent
introText=
{
$t
(
'
surveys.consent.intro
'
)
}
participation=
{
$t
(
'
surveys.consent.participation
'
)
}
participationD=
{
$t
(
'
surveys.consent.participationD
'
)
}
privacy=
{
$t
(
'
surveys.consent.privacy
'
)
}
privacyD=
{
$t
(
'
surveys.consent.privacyD
'
)
}
rights=
{
$t
(
'
surveys.consent.rights
'
)
}
/>
<div
class=
"form-control"
>
<button
class=
"button mt-4"
onclick=
{
()
=>
step
++
}
>
{
$t
(
'
surveys.consent.ok
'
)
}
</button>
</div>
</div>
{
:else
if
step
==
2
}
{
#if
currentGroup
.
demo
}
<div
class=
"mx-auto mt-10 text-center"
>
<p
class=
"text-center font-bold text-xl m-auto"
>
{
$t
(
'
surveys.example
'
)
}
</p>
</div>
{
/if
}
{
#if
type
==
'
gap
'
&&
gaps
}
<div
class=
"mx-auto mt-16 center flex flex-col"
>
<div>
{
#each
gaps
as
part
(
part
)
}
{
#if
part
.
gap
!==
null
}
<Gapfill
length=
{
part
.
text
.
length
}
onInput=
{
(
text
)
=>
(
part
.
gap
=
text
)
}
/>
{
:
else
}
{
part
.
text
}
{
/if
}
{
/each
}
</div>
<button
class=
"button mt-8"
onclick=
{
sendGap
}
>
{
$t
(
'
button.next
'
)
}
</button>
</div>
{
:
else
}
<div
class=
"mx-auto mt-16 text-center"
>
{
#if
type
==
'
text
'
}
<pre
class=
"text-center font-bold py-4 px-6 m-auto"
>
{
value
}
</pre>
{
:else
if
type
==
'
image
'
}
<img
src=
{
value
}
alt=
"Question"
/>
{
:else
if
type
==
'
audio
'
}
<audio
bind:this=
{
soundPlayer
}
controls
autoplay
class=
"rounded-lg mx-auto"
>
<source
src=
{
value
}
type=
"audio/mpeg"
/>
Your browser does not support the audio element.
</audio>
{
/if
}
</div>
<div
class=
"mx-auto mt-16"
>
<div
class=
"grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-4"
>
{
#each
displayQuestionOptions
as
option
,
i
(
option
)
}
{
@
const
type
=
option
.
split
(
'
:
'
)[
0
]
}
{
#if
type
==
'
dropdown
'
}
{
@
const
value
=
option
.
split
(
'
:
'
)[
1
].
split
(
'
,
'
)
}
<select
class=
"select select-bordered !ml-0"
id=
"dropdown"
name=
"dropdown"
bind:value=
{
displayQuestionOptions
[
i
]
}
onchange=
{
()
=>
selectOption
(
option
)
}
required
>
{
#each
value
as
op
}
<option
value=
{
op
}
>
{
op
}
</option>
{
/each
}
</select>
{
:else
if
type
==
'
radio
'
}
{
@
const
value
=
option
.
split
(
'
:
'
)[
1
].
split
(
'
,
'
)
}
{
#each
value
as
op
}
<label
class=
"radio-label"
>
<input
type=
"radio"
name=
"dropdown"
value=
{
op
}
onchange=
{
()
=>
selectOption
(
op
)
}
required
class=
"radio-button"
/>
{
op
}
</label>
{
/each
}
{
:
else
}
{
@
const
value
=
option
.
split
(
'
:
'
).
slice
(
1
).
join
(
'
:
'
)
}
<div
class=
"h-48 w-48 overflow-hidden rounded-lg border border-black"
onclick=
{
()
=>
selectOption
(
option
)
}
role=
"button"
onkeydown=
{
()
=>
selectOption
(
option
)
}
tabindex=
"0"
>
{
#if
type
===
'
text
'
}
<span
class=
"flex items-center justify-center h-full w-full text-2xl transition-transform duration-200 ease-in-out transform hover:scale-105"
>
{
value
}
</span>
{
:else
if
type
===
'
image
'
}
<img
src=
{
value
}
alt=
"Option {option}"
class=
"object-cover h-full w-full transition-transform duration-200 ease-in-out transform hover:scale-105"
/>
{
:else
if
type
==
'
audio
'
}
<audio
controls
class=
"w-full"
onclick=
{
(
e
)
=>
{
e
.
preventDefault
();
e
.
stopPropagation
();
}
}
>
<source
src=
{
value
}
type=
"audio/mpeg"
/>
Your browser does not support the audio element.
</audio>
{
/if
}
</div>
{
/if
}
{
/each
}
</div>
</div>
{
/if
}
{
:else
if
step
===
3
}
{
#if
currentGroup
.
id
===
1100
}
{
@
const
genderOptions
=
[
{
value
:
'
male
'
,
label
:
$t
(
'
surveys.genders.male
'
)
},
{
value
:
'
female
'
,
label
:
$t
(
'
surveys.genders.female
'
)
},
{
value
:
'
other
'
,
label
:
$t
(
'
surveys.genders.other
'
)
},
{
value
:
'
na
'
,
label
:
$t
(
'
surveys.genders.na
'
)
}
]
}
{
#if
subStep
===
0
}
<div
class=
"mx-auto mt-16 text-center px-4"
>
<p
class=
"text-center font-bold py-4 px-6 m-auto"
>
{
$t
(
'
surveys.birthYear
'
)
}
</p>
<Dropdown
values=
{
Array
.
from
({
length
:
82
},
(
_
,
i
)
=>
{
const
year
=
1931
+
i
;
return
{
value
:
year
,
display
:
year
};
}
).
reverse
()}
bind:option=
{
selectedOption
}
placeholder=
{
$t
(
'
surveys.birthYear
'
)
}
funct=
{
()
=>
selectAnswer
(
'
birthYear
'
,
selectedOption
)
}
></Dropdown>
</div>
{
:else
if
subStep
===
1
}
<div
class=
"mx-auto mt-16 text-center px-4"
>
<p
class=
"text-center font-bold py-4 px-6 m-auto"
>
{
$t
(
'
surveys.gender
'
)
}
</p>
<div
class=
"flex flex-col items-center space-y-4"
>
{
#each
genderOptions
as
{
value
,
label
}
}
<label
class=
"radio-label flex items-center space-x-2"
>
<input
type=
"radio"
name=
"gender"
{
value
}
onchange=
{
()
=>
selectAnswer
(
'
gender
'
,
value
)
}
required
class="radio-button"
/>
<span>
{
label
}
</span>
</label>
{
/each
}
</div>
</div>
{
:else
if
subStep
===
2
}
<div
class=
"mx-auto mt-16 text-center px-4"
>
<p
class=
"text-center font-bold py-4 px-6 m-auto"
>
{
$t
(
'
surveys.homeLanguage
'
)
}
</p>
<Dropdown
values=
{
Object
.
entries
(
config
.
PRIMARY_LANGUAGE
).
map
(([
code
,
name
])
=>
(
{
value
:
code
,
display
:
name
}
))}
bind:option=
{
selectedOption
}
placeholder=
{
$t
(
'
surveys.homeLanguage
'
)
}
funct=
{
()
=>
selectAnswer
(
'
primaryLanguage
'
,
selectedOption
)
}
></Dropdown>
</div>
{
:else
if
subStep
===
3
}
<div
class=
"mx-auto mt-16 text-center px-4"
>
<p
class=
"text-center font-bold py-4 px-6 m-auto"
>
{
$t
(
'
surveys.otherLanguage
'
)
}
</p>
<p
class=
"mb-6 text-sm text-gray-600 text-center"
>
{
$t
(
'
surveys.otherLanguageNote
'
)
}
</p>
<Dropdown
values=
{
[
{
value
:
'
none
'
,
display
:
'
/
'
},
...
Object
.
entries
(
config
.
PRIMARY_LANGUAGE
).
map
(([
code
,
name
])
=>
(
{
value
:
code
,
display
:
name
}
))
]}
bind:option=
{
selectedOption
}
placeholder=
{
$t
(
'
surveys.otherLanguage
'
)
}
funct=
{
()
=>
selectAnswer
(
'
other_language
'
,
selectedOption
)
}
></Dropdown>
</div>
{
:else
if
subStep
===
4
}
<div
class=
"mx-auto mt-16 text-center px-4"
>
<p
class=
"text-center font-bold py-4 px-6 m-auto"
>
{
$t
(
'
surveys.education.title
'
)
}
</p>
<Dropdown
values=
{
[
{
value
:
'
NoEducation
'
,
display
:
$t
(
'
surveys.education.NoEducation
'
)
},
{
value
:
'
PrimarySchool
'
,
display
:
$t
(
'
surveys.education.PrimarySchool
'
)
},
{
value
:
'
SecondarySchool
'
,
display
:
$t
(
'
surveys.education.SecondarySchool
'
)
},
{
value
:
'
NonUni
'
,
display
:
$t
(
'
surveys.education.NonUni
'
)
},
{
value
:
'
Bachelor
'
,
display
:
$t
(
'
surveys.education.Bachelor
'
)
},
{
value
:
'
Master
'
,
display
:
$t
(
'
surveys.education.Master
'
)
}
]
}
bind:option=
{
selectedOption
}
placeholder=
{
$t
(
'
surveys.education.title
'
)
}
funct=
{
()
=>
selectAnswer
(
'
education
'
,
selectedOption
)
}
></Dropdown>
</div>
{
/if
}
{
:
else
}
{
(
step
+=
1
)
}
{
/if
}
{
:else
if
step
===
4
}
<div
class=
"mx-auto mt-16 text-center"
>
<h1>
{
$t
(
'
surveys.complete
'
)
}
</h1>
{
#if
finalScore
!==
null
}
<p>
{
$t
(
'
surveys.score
'
)
}
<strong>
{
finalScore
}
%
</strong></p>
{
/if
}
</div>
{
#if
user
==
null
}
<footer
class=
"mt-auto text-center text-xs py-4"
>
{
$t
(
'
register.consent.studyData.person
'
)
}
:
{
$t
(
'
register.consent.studyData.personD
'
)
}
-
{
$t
(
'
register.consent.studyData.email
'
)
}
:
<a
href=
"mailto:{$t('register.consent.studyData.emailD')}"
class=
"link"
>
{
$t
(
'
register.consent.studyData.emailD
'
)
}
</a
>
</footer>
{
/if
}
{
/if
}
Chargement en cours