[Client] Improve questions on admin page + fix its JavaScript and deduplicate common style
- Use proper HTML; - Add CSS file specific to this page; - Deduplicate common style by moving it into the admin_ui CSS file and do the corresponding changes in the admin CSS and HTML files; - Add common header; - Move questions's JavaScript in a dedicated file, improve its code and fix its access to DOM elements.
This commit is contained in:
parent
d7ec28eacf
commit
4d46c62aae
@ -33,9 +33,24 @@ body {
|
|||||||
margin: var(--body-margin);
|
margin: var(--body-margin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 1em;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
button, input {
|
button, input {
|
||||||
|
background-color: transparent;
|
||||||
|
border-color: var(--admin-white-color);
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 0.125em;
|
||||||
|
color: var(--admin-white-color);
|
||||||
font-family: "Roboto Mono", sans-serif;
|
font-family: "Roboto Mono", sans-serif;
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
|
margin: 1em;
|
||||||
|
padding: 0.25em;
|
||||||
}
|
}
|
||||||
|
|
||||||
header {
|
header {
|
||||||
@ -56,11 +71,15 @@ header a {
|
|||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
header a:hover {
|
a:hover {
|
||||||
background-color: var(--admin-white-color);
|
background-color: var(--admin-white-color);
|
||||||
color: var(--admin-grey-color);
|
color: var(--admin-grey-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a:hover > .action_icon, .action_button:hover > .action_icon {
|
||||||
|
fill: var(--admin-grey-color);
|
||||||
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
background-color: var(--admin-grey-color);
|
background-color: var(--admin-grey-color);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@ -70,6 +89,17 @@ svg {
|
|||||||
fill: var(--admin-white-color);
|
fill: var(--admin-white-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.action_button:hover {
|
||||||
|
background-color: var(--admin-white-color);
|
||||||
|
color: var(--admin-grey-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.action_icon {
|
||||||
|
height: 2em;
|
||||||
|
transition-property: fill !important;
|
||||||
|
width: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
.page_category {
|
.page_category {
|
||||||
font-size: 2em;
|
font-size: 2em;
|
||||||
}
|
}
|
||||||
@ -112,20 +142,3 @@ svg {
|
|||||||
#section>input{
|
#section>input{
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.questionTypeTag{
|
|
||||||
border: thin solid red;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
.questionTypeTag input{
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.questionType{
|
|
||||||
border: thin solid red;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
.question input{
|
|
||||||
width: 100%;
|
|
||||||
margin: 10px;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,13 +1,3 @@
|
|||||||
.add_npc_icon {
|
|
||||||
height: 2em;
|
|
||||||
transition-property: fill;
|
|
||||||
width: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.add_npc_icon:hover {
|
|
||||||
transition-property: fill;
|
|
||||||
}
|
|
||||||
|
|
||||||
.add_npc_link {
|
.add_npc_link {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border-color: var(--admin-white-color);
|
border-color: var(--admin-white-color);
|
||||||
@ -24,26 +14,22 @@
|
|||||||
padding: 0.25em;
|
padding: 0.25em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.add_npc_link:hover {
|
|
||||||
background-color: var(--admin-white-color);
|
|
||||||
color: var(--admin-grey-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.add_npc_link:hover > .add_npc_icon {
|
|
||||||
fill: var(--admin-grey-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.character_item {
|
.character_item {
|
||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
margin: 1em;
|
margin: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.character_item a {
|
.character_item a {
|
||||||
|
padding: 0.25em;
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
text-decoration-color: var(--admin-white-color);
|
text-decoration-color: var(--admin-white-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.character_item a:hover {
|
||||||
|
text-decoration-color: var(--admin-grey-color);
|
||||||
|
}
|
||||||
|
|
||||||
.character_list {
|
.character_list {
|
||||||
align-content: center;
|
align-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@ -1,17 +1,3 @@
|
|||||||
button {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
button, input {
|
|
||||||
background-color: transparent;
|
|
||||||
border-color: var(--admin-white-color);
|
|
||||||
border-style: solid;
|
|
||||||
border-width: 0.125em;
|
|
||||||
color: var(--admin-white-color);
|
|
||||||
margin: 1em;
|
|
||||||
padding: 0.25em;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="text"] {
|
input[type="text"] {
|
||||||
width: 20em;
|
width: 20em;
|
||||||
}
|
}
|
||||||
@ -23,31 +9,6 @@ input[type="text"] {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action_button {
|
|
||||||
align-items: center;
|
|
||||||
border-radius: 1em;
|
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 0.25em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action_button:hover {
|
|
||||||
background-color: var(--admin-white-color);
|
|
||||||
color: var(--admin-grey-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.action_button:hover > .action_icon {
|
|
||||||
fill: var(--admin-grey-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.action_icon {
|
|
||||||
height: 2em;
|
|
||||||
transition-property: fill;
|
|
||||||
width: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.character_image {
|
.character_image {
|
||||||
width: 15em;
|
width: 15em;
|
||||||
}
|
}
|
||||||
|
|||||||
32
truthinquiry/static/css/admin_ui_questions.css
Normal file
32
truthinquiry/static/css/admin_ui_questions.css
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
.add_question_btn, #save_changes {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete_question_btn, .question_input {
|
||||||
|
margin: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.question {
|
||||||
|
align-content: center;
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.question_input {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.question_type {
|
||||||
|
border-color: var(--admin-white-color);
|
||||||
|
border-radius: 1em;
|
||||||
|
border-style: solid;
|
||||||
|
margin: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.question_type_title {
|
||||||
|
font-size: 1.5em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
@ -51,39 +51,3 @@ function saveFormTraits(){
|
|||||||
}
|
}
|
||||||
makeAPIRequest("admin/setTraits", {"traits": data, "lang": "FR"}, {"content": "json"})
|
makeAPIRequest("admin/setTraits", {"traits": data, "lang": "FR"}, {"content": "json"})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//functions for questions.html
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function addInputQuestions(button){
|
|
||||||
let questionTypeContent = button.parentNode.querySelector(".questionTypeContent");
|
|
||||||
let newQuestion = questionTypeContent.querySelector(".question").cloneNode(true);
|
|
||||||
newQuestion.id = "";
|
|
||||||
newQuestion.querySelector("input").value = "";
|
|
||||||
questionTypeContent.appendChild(newQuestion);
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteInputQuestions(buttonNode){
|
|
||||||
let placeNode = buttonNode.parentNode;
|
|
||||||
placeNode.parentNode.removeChild(placeNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
function saveFormQuestions(){
|
|
||||||
let data = [];
|
|
||||||
|
|
||||||
for(let questionTypeNode of allQuestions.querySelectorAll(".questionType")){
|
|
||||||
let questionsJson = [];
|
|
||||||
let questionTypeJson = {"questions": questionsJson};
|
|
||||||
data.push(questionTypeJson);
|
|
||||||
|
|
||||||
for(let questionNode of questionTypeNode.querySelectorAll("input")){
|
|
||||||
questionsJson.push({"text": questionNode.value})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
makeAPIRequest("admin/setQuestions", {"questions": data, "lang": "FR"}, {"content": "json"})
|
|
||||||
}
|
|
||||||
|
|||||||
95
truthinquiry/static/js/admin_questions.js
Normal file
95
truthinquiry/static/js/admin_questions.js
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
function addQuestion(questionAddButton) {
|
||||||
|
const questionElement = document.createElement("div");
|
||||||
|
questionElement.classList.add("question");
|
||||||
|
|
||||||
|
const inputElement = document.createElement("input");
|
||||||
|
inputElement.classList.add("question_input");
|
||||||
|
inputElement.setAttribute("type", "text");
|
||||||
|
|
||||||
|
questionElement.appendChild(inputElement);
|
||||||
|
|
||||||
|
const buttonElement = document.createElement("button");
|
||||||
|
buttonElement.classList.add("delete_question_btn", "action_button", "short_color_transition");
|
||||||
|
buttonElement.setAttribute("title", "Cliquez ici pour supprimer cette question");
|
||||||
|
|
||||||
|
const svgElement = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||||
|
svgElement.classList.add("action_icon", "short_color_transition");
|
||||||
|
svgElement.setAttribute("viewBox", "0 0 48 48");
|
||||||
|
|
||||||
|
const pathElement = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
||||||
|
pathElement.setAttribute("d",
|
||||||
|
"M12.45 38.7 9.3 35.55 20.85 24 9.3 12.5l3.15-3.2L24 20.8 35.55 9.3l3.15 3.2L27.2 24l11.5 11.55-3.15 3.15L24 27.2Z");
|
||||||
|
|
||||||
|
svgElement.appendChild(pathElement);
|
||||||
|
|
||||||
|
buttonElement.appendChild(svgElement);
|
||||||
|
buttonElement.appendChild(document.createTextNode("Supprimer la question"));
|
||||||
|
buttonElement.addEventListener("click", () => deleteQuestion(buttonElement));
|
||||||
|
|
||||||
|
questionElement.appendChild(buttonElement);
|
||||||
|
|
||||||
|
const questionTypeListElements = questionAddButton.parentNode.getElementsByClassName("question_type_list");
|
||||||
|
if (questionTypeListElements.length == 0) {
|
||||||
|
// No question_type_list element, this should never happen
|
||||||
|
// Do nothing in this case
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// There should be at most one question_type_list element per question type
|
||||||
|
questionTypeListElements[0].appendChild(questionElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteQuestion(questionRemoveButton) {
|
||||||
|
if (!confirm("Voulez-vous vraiement supprimer ce lieu ?")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const questionElement = questionRemoveButton.parentNode;
|
||||||
|
questionElement.parentNode.removeChild(questionElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveChanges() {
|
||||||
|
const data = [];
|
||||||
|
|
||||||
|
for (const questionTypeNode of document.getElementsByClassName("question_type")) {
|
||||||
|
const questionsJson = [];
|
||||||
|
|
||||||
|
for (const questionNode of questionTypeNode.querySelectorAll(
|
||||||
|
".question_type_list .question .question_input")) {
|
||||||
|
questionsJson.push({"text": questionNode.value});
|
||||||
|
}
|
||||||
|
|
||||||
|
const questionTypeJson = {"questions": questionsJson};
|
||||||
|
data.push(questionTypeJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
makeAPIRequest("admin/setQuestions", {"questions": data, "lang": "FR"}, {"content": "json"}).then(() => {
|
||||||
|
alert("Opération effectuée avec succès");
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function setListenersToQuestionAdditionButtons() {
|
||||||
|
for (const deleteQuestionButton of document.getElementsByClassName("add_question_btn")) {
|
||||||
|
deleteQuestionButton.addEventListener("click", () => addQuestion(deleteQuestionButton));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function setListenersToQuestionDeletionButtons() {
|
||||||
|
for (const deleteQuestionButton of document.getElementsByClassName("delete_question_btn")) {
|
||||||
|
deleteQuestionButton.addEventListener("click", () => deleteQuestion(deleteQuestionButton));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function setListenersToSaveChangesButton() {
|
||||||
|
const saveChangesButton = document.getElementById("save_changes");
|
||||||
|
if (saveChangesButton === null) {
|
||||||
|
// There is no save_changes button, this should never happen
|
||||||
|
// Do nothing in this case
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
saveChangesButton.addEventListener("click", saveChanges);
|
||||||
|
}
|
||||||
|
|
||||||
|
setListenersToQuestionAdditionButtons();
|
||||||
|
setListenersToQuestionDeletionButtons();
|
||||||
|
setListenersToSaveChangesButton();
|
||||||
@ -29,12 +29,12 @@
|
|||||||
<ul class="character_list">
|
<ul class="character_list">
|
||||||
{%for npc in npcs%}
|
{%for npc in npcs%}
|
||||||
<li class="character_item">
|
<li class="character_item">
|
||||||
<a href="/admin/npc/{{npc['id']}}" title="Cliquez ici pour gérer le personnage « {{npc['name']}} »">{{npc['name']}}</a>
|
<a class="short_color_transition" href="/admin/npc/{{npc['id']}}" title="Cliquez ici pour gérer le personnage « {{npc['name']}} »">{{npc['name']}}</a>
|
||||||
</li>
|
</li>
|
||||||
{%endfor%}
|
{%endfor%}
|
||||||
</ul>
|
</ul>
|
||||||
<a class="add_npc_link short_color_transition" href="/admin/npc/new" title="Cliquez ici pour créer un nouveau personnage">
|
<a class="add_npc_link short_color_transition" href="/admin/npc/new" title="Cliquez ici pour créer un nouveau personnage">
|
||||||
<svg class="add_npc_icon short_color_transition" xmlns="http://www.w3.org/2000/svg" viewBox="0 96 960 960">
|
<svg class="action_icon short_color_transition" xmlns="http://www.w3.org/2000/svg" viewBox="0 96 960 960">
|
||||||
<path d="M435 871V622H185v-91h250V281h91v250h250v91H526v249h-91Z"/>
|
<path d="M435 871V622H185v-91h250V281h91v250h250v91H526v249h-91Z"/>
|
||||||
</svg>
|
</svg>
|
||||||
Nouveau personnage
|
Nouveau personnage
|
||||||
|
|||||||
@ -1,34 +1,69 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="fr">
|
<html lang="fr">
|
||||||
<head>
|
<head>
|
||||||
<title>Admin Page</title>
|
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<link rel="stylesheet" href="admin_ui.css">
|
<title>Truth Inquiry - Gestion des questions</title>
|
||||||
<script src="/static/js/admin.js"></script>
|
<link rel="stylesheet" href="/static/css/admin_ui.css">
|
||||||
<script src="/static/js/api.js"></script>
|
<link rel="stylesheet" href="/static/css/admin_ui_questions.css">
|
||||||
|
<link rel="icon" href="/static/images/favicon/favicon_32.png" type="image/png" sizes="32x32">
|
||||||
|
<link rel="icon" href="/static/images/favicon/favicon_64.png" type="image/png" sizes="64x64">
|
||||||
|
<link rel="icon" href="/static/images/favicon/favicon_96.png" type="image/png" sizes="96x96">
|
||||||
|
<link rel="icon" href="/static/images/favicon/favicon_128.png" type="image/png" sizes="128x128">
|
||||||
|
<link rel="icon" href="/static/images/favicon/favicon_192.png" type="image/png" sizes="192x192">
|
||||||
|
<link rel="icon" href="/static/images/favicon/favicon_256.png" type="image/png" sizes="256x256">
|
||||||
|
<link rel="icon" href="/static/images/favicon/favicon_256.png" type="image/png" sizes="512x512">
|
||||||
|
<meta name="color-scheme" content="dark light">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<header>
|
||||||
<a href="/admin"> go Back </a> <br>
|
<a class="short_color_transition" href="/admin" title="Cliquez ici pour revenir à l'accueil de l'interface d'administration du jeu">Accueil</a>
|
||||||
|
<a class="short_color_transition" href="/admin/questions" title="Cliquez ici pour gérer les questions du jeu">Gestion des questions</a>
|
||||||
<section id="allQuestions">
|
<a class="short_color_transition" href="/admin/places" title="Cliquez ici pour gérer les lieux du jeu">Gestion des lieux</a>
|
||||||
{%for questionType in questions%}
|
<a class="short_color_transition" href="/admin/traits" title="Cliquez ici pour gérer les réactions du jeu">Gestion des réactions</a>
|
||||||
<div class="questionType">
|
<a class="short_color_transition" href="/api/v1/admin/logout" title="Cliquez ici pour vous déconnecter de l'interface d'administration du jeu">Déconnexion</a>
|
||||||
<section class="questionTypeContent">
|
</header>
|
||||||
{%for question in questionType["questions"]%}
|
<h1 class="page_title">Truth Inquiry - Interface d'administration</h1>
|
||||||
<section class="question">
|
<h2 class="page_category">Gestion des questions</h2>
|
||||||
<input value="{{question['text']}}">
|
<p class="page_description">Cliquez sur les champs pour éditer les questions.</p>
|
||||||
<button onclick="deleteInputQuestions(this)">Delete question</button>
|
{%for questionType in questions%}
|
||||||
</section>
|
<section class="question_type">
|
||||||
{%endfor%}
|
<h3 class="question_type_title">Type de question {{loop.index0}}</h3>
|
||||||
</section>
|
<div class="question_type_list">
|
||||||
<button onclick="addInputQuestions(this)">Add new</button>
|
{%for question in questionType["questions"]%}
|
||||||
|
<div class="question">
|
||||||
|
<input class="question_input" type="text" value="{{question['text']}}">
|
||||||
|
<button class="delete_question_btn action_button short_color_transition" title="Cliquez ici pour supprimer cette question">
|
||||||
|
<svg class="action_icon short_color_transition" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 48 48">
|
||||||
|
<path d="M12.45 38.7 9.3 35.55 20.85 24 9.3 12.5l3.15-3.2L24 20.8 35.55 9.3l3.15 3.2L27.2 24l11.5 11.55-3.15 3.15L24 27.2Z"/>
|
||||||
|
</svg>
|
||||||
|
Supprimer la question
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{%endfor%}
|
||||||
</div>
|
</div>
|
||||||
{%endfor%}
|
<button class="add_question_btn action_button short_color_transition" title="Cliquez ici pour ajouter une question au type {{loop.index0}}">
|
||||||
|
<svg class="action_icon short_color_transition" xmlns="http://www.w3.org/2000/svg" viewBox="0 96 960 960">
|
||||||
|
<path d="M435 871V622H185v-91h250V281h91v250h250v91H526v249h-91Z"/>
|
||||||
|
</svg>
|
||||||
|
Ajouter une question
|
||||||
|
</button>
|
||||||
</section>
|
</section>
|
||||||
|
{%endfor%}
|
||||||
<br>
|
<button id="save_changes" class="action_button short_color_transition" title="Cliquez ici pour enregistrer vos changements">
|
||||||
|
<svg class="action_icon short_color_transition" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 48 48">
|
||||||
<button onclick="saveFormQuestions()"> Save changes </button>
|
<path d="M18.9 36.75 6.65 24.5l3.3-3.3 8.95 9L38 11.1l3.3 3.25Z"/>
|
||||||
|
</svg>
|
||||||
|
Sauvegarder les changements
|
||||||
|
</button>
|
||||||
|
<noscript>
|
||||||
|
<div class="alert_dialog_background"></div>
|
||||||
|
<dialog>
|
||||||
|
<h3 class="alert_dialog_title">JavaScript nécessaire</h3>
|
||||||
|
<p class="alert_dialog_msg unsupported_browser_msg">Désolé, mais JavaScript est nécessaire pour faire fonctionner cette page. Veuillez l'activer dans votre navigateur ou en utiliser un qui le supporte afin de pouvoir gérer les questions.</p>
|
||||||
|
</dialog>
|
||||||
|
</noscript>
|
||||||
|
<script src="/static/js/api.js"></script>
|
||||||
|
<script src="/static/js/admin_questions.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user