Merge pull request #148 from ThomasRubini/client_admin-pages-improvements-part-2
[Client] Second part of admin pages improvements
This commit is contained in:
		
						commit
						7e64dc69d3
					
				| @ -9,10 +9,12 @@ | ||||
| } | ||||
| 
 | ||||
| :root { | ||||
|     --admin-black-color: #000000; | ||||
|     --admin-grey-color: #5A5656; | ||||
|     --admin-red-color: #FF0000; | ||||
|     --admin-white-color: #FFFFFF; | ||||
|     --adming-yellow-color: #FFC800; | ||||
|     --adming-yellow-color: #D6A851; | ||||
|     --alert-dialog-background-color: #000000DF; | ||||
|     --body-margin: 0.375em; | ||||
|     font-family: "Roboto Mono", sans-serif; | ||||
| } | ||||
| @ -25,6 +27,15 @@ a, body { | ||||
|     color: var(--admin-white-color); | ||||
| } | ||||
| 
 | ||||
| a:focus, a:hover { | ||||
|     background-color: var(--admin-white-color); | ||||
|     color: var(--admin-grey-color) !important; | ||||
| } | ||||
| 
 | ||||
| a:focus > .action_icon, .action_button:focus > .action_icon, a:hover > .action_icon, .action_button:hover > .action_icon { | ||||
|     fill: var(--admin-grey-color); | ||||
| } | ||||
| 
 | ||||
| a:visited { | ||||
|     color: unset; | ||||
| } | ||||
| @ -33,9 +44,24 @@ body { | ||||
|     margin: var(--body-margin); | ||||
| } | ||||
| 
 | ||||
| button, input { | ||||
| button { | ||||
|     align-items: center; | ||||
|     border-radius: 1em; | ||||
|     cursor: pointer; | ||||
|     display: flex; | ||||
|     justify-content: center; | ||||
| } | ||||
| 
 | ||||
| button, input, select { | ||||
|     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-size: 1em; | ||||
|     margin: 1em; | ||||
|     padding: 0.25em; | ||||
| } | ||||
| 
 | ||||
| header { | ||||
| @ -56,20 +82,74 @@ header a { | ||||
|     text-decoration: none; | ||||
| } | ||||
| 
 | ||||
| header a:hover { | ||||
|     background-color: var(--admin-white-color); | ||||
|     color: var(--admin-grey-color); | ||||
| } | ||||
| 
 | ||||
| html { | ||||
|     background-color: var(--admin-grey-color); | ||||
|     margin: 0; | ||||
| } | ||||
| 
 | ||||
| noscript dialog { | ||||
|     align-items: center; | ||||
|     background-color: var(--admin-grey-color); | ||||
|     border-color: var(--admin-white-color); | ||||
|     border-radius: 1em; | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
|     flex-wrap: nowrap; | ||||
|     justify-content: center; | ||||
|     left: 50%; | ||||
|     overflow: auto; | ||||
|     padding: 2em; | ||||
|     position: fixed; | ||||
|     text-align: center; | ||||
|     top: 50%; | ||||
|     transform: translate(-50%, -50%); | ||||
|     z-index: 2; | ||||
| } | ||||
| 
 | ||||
| svg { | ||||
|     fill: var(--admin-white-color); | ||||
| } | ||||
| 
 | ||||
| .action_button:focus, .action_button:hover { | ||||
|     background-color: var(--admin-white-color); | ||||
|     color: var(--admin-grey-color); | ||||
| } | ||||
| 
 | ||||
| .action_icon { | ||||
|     height: 2em; | ||||
|     transition-property: fill !important; | ||||
|     width: 2em; | ||||
| } | ||||
| 
 | ||||
| .alert_dialog_background { | ||||
|     background-color: var(--alert-dialog-background-color); | ||||
|     display: block; | ||||
|     height: 100vh; | ||||
|     left: 0; | ||||
|     position: fixed; | ||||
|     top: 0; | ||||
|     width: 100vw; | ||||
|     z-index: 1; | ||||
| } | ||||
| 
 | ||||
| .alert_dialog_bottom_link { | ||||
|     font-size: 1.125em; | ||||
|     padding: 0.25em; | ||||
|     text-decoration: underline; | ||||
| } | ||||
| 
 | ||||
| .alert_dialog_msg { | ||||
|     font-size: 1.25em; | ||||
| } | ||||
| 
 | ||||
| .alert_dialog_msg, .alert_dialog_title { | ||||
|     margin: 1em; | ||||
| } | ||||
| 
 | ||||
| .alert_dialog_title { | ||||
|     font-size: 1.75em; | ||||
| } | ||||
| 
 | ||||
| .page_category { | ||||
|     font-size: 2em; | ||||
| } | ||||
| @ -102,30 +182,3 @@ svg { | ||||
|     transition-duration: 0.25s; | ||||
|     transition-timing-function: linear; | ||||
| } | ||||
| 
 | ||||
| #places>section { | ||||
|     border: thin solid red; | ||||
|     padding: 5px; | ||||
|     margin-top: 20px; | ||||
| } | ||||
| 
 | ||||
| #section>input{ | ||||
|     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 { | ||||
|     align-items: center; | ||||
|     border-color: var(--admin-white-color); | ||||
| @ -24,26 +14,22 @@ | ||||
|     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 { | ||||
|     font-size: 1.5em; | ||||
|     list-style-type: none; | ||||
|     margin: 1em; | ||||
|     margin: 0.5em; | ||||
| } | ||||
| 
 | ||||
| .character_item a { | ||||
|     padding: 0.25em; | ||||
|     text-decoration: underline; | ||||
|     text-decoration-color: var(--admin-white-color); | ||||
| } | ||||
| 
 | ||||
| .character_item a:hover { | ||||
|     text-decoration-color: var(--admin-grey-color); | ||||
| } | ||||
| 
 | ||||
| .character_list { | ||||
|     align-content: center; | ||||
|     align-items: center; | ||||
|  | ||||
| @ -31,3 +31,9 @@ input { | ||||
|     flex-direction: row; | ||||
|     justify-content: center; | ||||
| } | ||||
| 
 | ||||
| #login_input_button { | ||||
|     cursor: pointer; | ||||
|     border-radius: 1em; | ||||
|     padding: 0.25em; | ||||
| } | ||||
|  | ||||
| @ -1,55 +1,36 @@ | ||||
| button { | ||||
|     display: flex; | ||||
| img[alt] { | ||||
|     font-size: 1em; | ||||
| } | ||||
| 
 | ||||
| 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; | ||||
| img[alt], .info_item, .section_title { | ||||
|     text-align: center; | ||||
| } | ||||
| 
 | ||||
| input[type="text"] { | ||||
|     width: 20em; | ||||
| } | ||||
| 
 | ||||
| .action_buttons, .answer_groups { | ||||
|     align-content: center; | ||||
|     display: flex; | ||||
|     flex-wrap: wrap; | ||||
|     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 { | ||||
| option { | ||||
|     background-color: var(--admin-white-color); | ||||
|     color: var(--admin-grey-color); | ||||
|     color: var(--admin-black-color); | ||||
| } | ||||
| 
 | ||||
| .action_button:hover > .action_icon { | ||||
|     fill: var(--admin-grey-color); | ||||
| .action_buttons, .answer_groups, .reaction, #add_reactions { | ||||
|     flex-wrap: wrap; | ||||
| } | ||||
| 
 | ||||
| .action_icon { | ||||
|     height: 2em; | ||||
|     transition-property: fill; | ||||
|     width: 2em; | ||||
| .action_buttons, .answer_group, .answer_groups, .reaction, #add_reactions { | ||||
|     display: flex; | ||||
|     justify-content: center; | ||||
| } | ||||
| 
 | ||||
| .character_image { | ||||
|     width: 15em; | ||||
| .answer_group, .reaction, #add_reactions { | ||||
|     align-items: center; | ||||
|     flex-direction: column; | ||||
| } | ||||
| 
 | ||||
| .character_image, .reaction_image { | ||||
|     width: 20em; | ||||
| } | ||||
| 
 | ||||
| .character_image, #npc_name { | ||||
| @ -65,8 +46,8 @@ input[type="text"] { | ||||
|     font-size: 1.25em; | ||||
| } | ||||
| 
 | ||||
| .info_item, .section_title { | ||||
|     text-align: center; | ||||
| .reaction, #add_reactions { | ||||
|     align-content: center; | ||||
| } | ||||
| 
 | ||||
| .section_title { | ||||
|  | ||||
							
								
								
									
										16
									
								
								truthinquiry/static/css/admin_ui_places.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								truthinquiry/static/css/admin_ui_places.css
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | ||||
| .place { | ||||
|     align-content: center; | ||||
|     align-items: center; | ||||
|     display: flex; | ||||
|     flex-wrap: wrap; | ||||
|     justify-content: center; | ||||
| } | ||||
| 
 | ||||
| .place_input { | ||||
|     flex-grow: 1; | ||||
| } | ||||
| 
 | ||||
| #add_place, #save_changes { | ||||
|     margin-left: auto; | ||||
|     margin-right: auto; | ||||
| } | ||||
							
								
								
									
										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; | ||||
| } | ||||
							
								
								
									
										17
									
								
								truthinquiry/static/css/admin_ui_traits.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								truthinquiry/static/css/admin_ui_traits.css
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| .delete_trait_btn, #add_trait, #save_changes { | ||||
|     margin-left: auto; | ||||
|     margin-right: auto; | ||||
| } | ||||
| 
 | ||||
| .trait_description_container, .trait_name_container { | ||||
|     display: flex; | ||||
|     flex-wrap: wrap; | ||||
|     align-content: center; | ||||
|     justify-content: center; | ||||
|     align-items: center; | ||||
|     flex-direction: row; | ||||
| } | ||||
| 
 | ||||
| .trait_description_input, .trait_name_input { | ||||
|     width: 30em; | ||||
| } | ||||
| @ -1,89 +0,0 @@ | ||||
| //functions for places.html
 | ||||
| 
 | ||||
| function addInputPlaces(){ | ||||
|     let newPlace = places.lastElementChild.cloneNode(true); | ||||
|     newPlace.id = ""; | ||||
|     newPlace.querySelector("input").value = ""; | ||||
|     places.appendChild(newPlace); | ||||
| } | ||||
| 
 | ||||
| function deleteInputPlaces(buttonNode){ | ||||
|     let placeNode = buttonNode.parentNode; | ||||
|     placeNode.parentNode.removeChild(placeNode); | ||||
| } | ||||
| 
 | ||||
| function saveFormPlaces(){ | ||||
|     let data = []; | ||||
|     for(let section of places.querySelectorAll("section")){ | ||||
|         let place = {}; | ||||
|         place["id"] = section.id | ||||
|         place["name"] = section.querySelector("input").value | ||||
|         data.push(place); | ||||
|     } | ||||
|     makeAPIRequest("admin/setPlaces", {"places": data, "lang": "FR"}, {"content": "json"}) | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| //functions for traits.html
 | ||||
| 
 | ||||
| 
 | ||||
| function addInputTraits(){ | ||||
|     let newTrait = traits.lastElementChild.cloneNode(true); | ||||
|     newTrait.id = ""; | ||||
|     newTrait.querySelector(".name_input").value = ""; | ||||
|     newTrait.querySelector(".desc_input").value = ""; | ||||
|     traits.appendChild(newTrait); | ||||
| } | ||||
| 
 | ||||
| function deleteInputTraits(buttonNode){ | ||||
|     let traitNode = buttonNode.parentNode; | ||||
|     traitNode.parentNode.removeChild(traitNode); | ||||
| } | ||||
| 
 | ||||
| function saveFormTraits(){ | ||||
|     let data = []; | ||||
|     for(let section of traits.querySelectorAll("section")){ | ||||
|         let trait = {}; | ||||
|         trait["id"] = section.id | ||||
|         trait["name"] = section.querySelector(".name_input").value | ||||
|         trait["desc"] = section.querySelector(".desc_input").value | ||||
|         data.push(trait); | ||||
|     } | ||||
|     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"}) | ||||
| } | ||||
| @ -1,5 +1,5 @@ | ||||
| 
 | ||||
| const reactionsDelta = {} | ||||
| const reactionsDelta = {}; | ||||
| 
 | ||||
| async function createOrUpdateNpc() { | ||||
|     const data = {}; | ||||
| @ -9,7 +9,7 @@ async function createOrUpdateNpc() { | ||||
|     const allAnswersJson = []; | ||||
|     data["allAnswers"] = allAnswersJson; | ||||
|      | ||||
|     for (let answerTypeNode of document.querySelector(".answer_groups").children) { | ||||
|     for (const answerTypeNode of document.querySelector(".answer_groups").children) { | ||||
|         const answersJson = []; | ||||
|         const answerTypeJson = {"answers": answersJson}; | ||||
|         allAnswersJson.push(answerTypeJson); | ||||
| @ -20,35 +20,33 @@ async function createOrUpdateNpc() { | ||||
|     } | ||||
| 
 | ||||
|     await makeAPIRequest("admin/setNpc", {"npc": data, "lang": "FR"}, {"content": "json"}); | ||||
| 
 | ||||
|     await uploadReactionsDelta(); | ||||
| 
 | ||||
|     alert("Opération effectuée avec succès"); | ||||
| } | ||||
| 
 | ||||
| async function uploadReactionsDelta() { | ||||
|     let requests = []; | ||||
|     const requests = []; | ||||
|     const npcId = document.querySelector("#npc_id").value; | ||||
| 
 | ||||
| 
 | ||||
|     for(const [traitId, reactionNode] of Object.entries(reactionsDelta)){ | ||||
|     for (const [traitId, reactionNode] of Object.entries(reactionsDelta)) { | ||||
|         const formData = new FormData(); | ||||
|         formData.append("npc_id", npc_id.value); | ||||
|         formData.append("npc_id", npcId); | ||||
|         formData.append("trait_id", traitId); | ||||
|          | ||||
|         if(reactionNode === null) formData.append("file", "null"); | ||||
|         else{ | ||||
|             const file = reactionNode.querySelector(".img_input").files[0] | ||||
|         if (reactionNode === null) { | ||||
|             formData.append("file", "null"); | ||||
|         } else { | ||||
|             const file = reactionNode.querySelector(".img_input").files[0]; | ||||
|             formData.append("file", file ? file : ""); | ||||
|         } | ||||
| 
 | ||||
|         requests.push(makeAPIRequest("admin/setReaction", formData, {"content": "form"})); | ||||
|     } | ||||
| 
 | ||||
|     for(request of requests){ | ||||
|     for (const request of requests) { | ||||
|         await request; | ||||
|     } | ||||
| 
 | ||||
|      | ||||
| } | ||||
| 
 | ||||
| async function deleteNpc() { | ||||
| @ -62,51 +60,141 @@ async function deleteNpc() { | ||||
|     document.location = "/admin"; | ||||
| } | ||||
| 
 | ||||
| function changeReaction(inputNode){ | ||||
|     const parentNode = inputNode.parentNode; | ||||
| function changeImageReaction(imageInputElement) { | ||||
|     const parentNode = imageInputElement.parentNode; | ||||
|     const imgNode = parentNode.querySelector('img'); | ||||
|     const traitId = parentNode.querySelector('.trait_id').value; | ||||
|      | ||||
|     const reader = new FileReader(); | ||||
|     reader.onload = (e)=>{ | ||||
|         imgNode.src = e.target.result | ||||
|     } | ||||
|     reader.readAsDataURL(inputNode.files[0]); | ||||
|     reader.addEventListener("load", event => { | ||||
|         imgNode.src = event.target.result | ||||
|     }); | ||||
|     reader.readAsDataURL(imageInputElement.files[0]); | ||||
| 
 | ||||
|     reactionsDelta[traitId] = parentNode; | ||||
| } | ||||
| 
 | ||||
| function deleteReaction(node){ | ||||
|     const reactionNode = node.parentNode; | ||||
| function deleteImageReaction(reactionDeletionButton) { | ||||
|     if (!confirm("Voulez-vous vraiment supprimer l'image de cette réaction ?")) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     const reactionNode = reactionDeletionButton.parentNode; | ||||
|     const traitId = reactionNode.querySelector(".trait_id").value; | ||||
|     const reactionName = reactionNode.querySelector("p").innerText; | ||||
|     const reactionName = reactionNode.querySelector(".reaction_name").innerText; | ||||
| 
 | ||||
|     reactionNode.parentNode.removeChild(reactionNode); | ||||
|      | ||||
|     const option = document.createElement("option"); | ||||
|     option.value = traitId | ||||
|     option.innerText = reactionName | ||||
|     option.value = traitId; | ||||
|     option.innerText = reactionName; | ||||
| 
 | ||||
|     reactions_to_add.appendChild(option); | ||||
|     const addReactionsSelectorElement = document.getElementById("add_reactions_selector"); | ||||
|     if (addReactionsSelectorElement === null) { | ||||
|         // No add_reactions_selector element, this should never happen
 | ||||
|         // Do nothing in this case
 | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     addReactionsSelectorElement.appendChild(option); | ||||
| 
 | ||||
|     reactionsDelta[traitId] = null; | ||||
| } | ||||
| 
 | ||||
| function addReaction(selectNode){ | ||||
|     const selectedOptionNode = selectNode.selectedOptions[0]; | ||||
| function addReaction(addReactionsSelectorElement) { | ||||
|     const selectedOptionNode = addReactionsSelectorElement.selectedOptions[0]; | ||||
|      | ||||
|     const traitId = selectedOptionNode.value; | ||||
|     const reactionName = selectedOptionNode.innerText; | ||||
| 
 | ||||
|     selectNode.removeChild(selectedOptionNode); | ||||
|     addReactionsSelectorElement.removeChild(selectedOptionNode); | ||||
| 
 | ||||
|     const newReaction = reactions.querySelector("div").cloneNode(true); | ||||
|     newReaction.querySelector("img").src = ""; | ||||
|     newReaction.querySelector(".img_input").value = null; | ||||
|     newReaction.querySelector(".trait_id").value = traitId | ||||
|     newReaction.querySelector("p").innerText = reactionName | ||||
|     const newReactionElement = document.createElement("section"); | ||||
|     newReactionElement.classList.add("reaction"); | ||||
| 
 | ||||
|     reactions.appendChild(newReaction); | ||||
|     const reactionNameElement = document.createElement("h3"); | ||||
|     reactionNameElement.classList.add("reaction_name"); | ||||
|     reactionNameElement.textContent = reactionName; | ||||
| 
 | ||||
|     reactionsDelta[traitId] = newReaction; | ||||
|     newReactionElement.appendChild(reactionNameElement); | ||||
| 
 | ||||
|     const imageElement = document.createElement("img"); | ||||
|     imageElement.classList.add("reaction_image"); | ||||
|     imageElement.setAttribute("alt", "Image d'une réaction d'un personnage"); | ||||
|     imageElement.src = "/static/images/no_photography_white.svg"; | ||||
| 
 | ||||
|     newReactionElement.appendChild(imageElement); | ||||
| 
 | ||||
|     const imageInputElement = document.createElement("input"); | ||||
|     imageInputElement.classList.add("img_input"); | ||||
|     imageInputElement.setAttribute("type", "file"); | ||||
|     imageInputElement.setAttribute("accept", "image/png, image/jpg, image/jpeg"); | ||||
|     imageInputElement.addEventListener("change", () => changeImageReaction(imageInputElement)); | ||||
| 
 | ||||
|     newReactionElement.appendChild(imageInputElement); | ||||
| 
 | ||||
|     const traitIdInputElement = document.createElement("input"); | ||||
|     traitIdInputElement.classList.add("trait_id"); | ||||
|     traitIdInputElement.setAttribute("type", "hidden"); | ||||
|     traitIdInputElement.setAttribute("value", traitId); | ||||
| 
 | ||||
|     newReactionElement.appendChild(traitIdInputElement); | ||||
| 
 | ||||
|     const buttonElement = document.createElement("button"); | ||||
|     buttonElement.classList.add("delete_question_btn", "action_button", "short_color_transition"); | ||||
|     buttonElement.setAttribute("title", "Cliquez ici pour supprimer l'image de cette réaction"); | ||||
|     buttonElement.addEventListener("click", () => deleteImageReaction(buttonElement)); | ||||
| 
 | ||||
|     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 l'image de la réaction")); | ||||
| 
 | ||||
|     newReactionElement.appendChild(buttonElement); | ||||
| 
 | ||||
|     const reactionsElement = document.getElementById("reactions"); | ||||
|     if (reactionsElement === null) { | ||||
|         // No add_reactions_selector element, this should never happen
 | ||||
|         // Do nothing in this case
 | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     reactionsElement.appendChild(newReactionElement); | ||||
| 
 | ||||
|     reactionsDelta[traitId] = newReactionElement; | ||||
| } | ||||
| 
 | ||||
| function setListenersToImageInputs() { | ||||
|     for (const imageInput of document.getElementsByClassName("img_input")) { | ||||
|         imageInput.addEventListener("change", () => changeImageReaction(imageInput)); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| function setListenersToImageReactionsRemovalButtons() { | ||||
|     for (const imageReactionRemovalButton of document.getElementsByClassName("delete_image_reaction_btn")) { | ||||
|         imageReactionRemovalButton.addEventListener("click", () => deleteImageReaction(imageReactionRemovalButton)); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| function setListenersToAddReactionsSelector() { | ||||
|     const addReactionsSelectorElement = document.getElementById("add_reactions_selector"); | ||||
|     if (addReactionsSelectorElement === null) { | ||||
|         // No add_reactions_selector element, this should never happen
 | ||||
|         // Do nothing in this case
 | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     addReactionsSelectorElement.addEventListener("change", () => addReaction(addReactionsSelectorElement)); | ||||
| } | ||||
| 
 | ||||
| setListenersToImageReactionsRemovalButtons(); | ||||
| setListenersToImageInputs(); | ||||
| setListenersToAddReactionsSelector(); | ||||
|  | ||||
							
								
								
									
										95
									
								
								truthinquiry/static/js/admin_places.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								truthinquiry/static/js/admin_places.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,95 @@ | ||||
| function addPlace() { | ||||
|     const placeElement = document.createElement("div"); | ||||
|     placeElement.classList.add("place"); | ||||
| 
 | ||||
|     const inputElement = document.createElement("input"); | ||||
|     inputElement.classList.add("place_input"); | ||||
|     inputElement.setAttribute("type", "text"); | ||||
|     inputElement.setAttribute("id", ""); | ||||
| 
 | ||||
|     placeElement.appendChild(inputElement); | ||||
| 
 | ||||
|     const buttonElement = document.createElement("button"); | ||||
|     buttonElement.classList.add("delete_place_btn", "action_button", "short_color_transition"); | ||||
|     buttonElement.setAttribute("title", "Cliquez ici pour supprimer ce lieu"); | ||||
| 
 | ||||
|     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 le lieu")); | ||||
|     buttonElement.addEventListener("click", () => deletePlace(buttonElement)); | ||||
| 
 | ||||
|     placeElement.appendChild(buttonElement); | ||||
| 
 | ||||
|     const placesElement = document.getElementById("places"); | ||||
|     if (placesElement === null) { | ||||
|         // No places element, this should never happen
 | ||||
|         // Do nothing in this case
 | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     placesElement.appendChild(placeElement); | ||||
| } | ||||
| 
 | ||||
| function deletePlace(placeRemoveButton) { | ||||
|     if (!confirm("Voulez-vous vraiement supprimer ce lieu ?")) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     const placeElement = placeRemoveButton.parentNode; | ||||
|     placeElement.parentNode.removeChild(placeElement); | ||||
| } | ||||
| 
 | ||||
| function saveChanges() { | ||||
|     const data = []; | ||||
|     for (const section of document.getElementsByClassName("place")) { | ||||
|         const place = {}; | ||||
|         place["id"] = section.id; | ||||
|         place["name"] = section.querySelector("input").value; | ||||
|         data.push(place); | ||||
|     } | ||||
| 
 | ||||
|     makeAPIRequest("admin/setPlaces", {"places": data, "lang": "FR"}, {"content": "json"}).then(() => { | ||||
|         alert("Opération effectuée avec succès"); | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| function setListenersToPlaceAdditionButton() { | ||||
|     const addPlaceButton = document.getElementById("add_place"); | ||||
|     if (addPlaceButton === null) { | ||||
|         // There is no add_place button, this should never happen
 | ||||
|         // Do nothing in this case
 | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     addPlaceButton.addEventListener("click", addPlace); | ||||
| } | ||||
| 
 | ||||
| function setListenersToPlaceDeletionButtons() { | ||||
|     for (const deletePlaceButton of document.getElementsByClassName("delete_place_btn")) { | ||||
|         deletePlaceButton.addEventListener("click", () => deletePlace(deletePlaceButton)); | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| 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); | ||||
| } | ||||
| 
 | ||||
| setListenersToPlaceDeletionButtons(); | ||||
| setListenersToPlaceAdditionButton(); | ||||
| setListenersToSaveChangesButton(); | ||||
							
								
								
									
										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(); | ||||
							
								
								
									
										127
									
								
								truthinquiry/static/js/admin_traits.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								truthinquiry/static/js/admin_traits.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,127 @@ | ||||
| function addTrait() { | ||||
|     const traitElement = document.createElement("div"); | ||||
|     traitElement.classList.add("trait"); | ||||
|     traitElement.setAttribute("data-id", ""); | ||||
| 
 | ||||
|     const traitNameContainerElement = document.createElement("div"); | ||||
|     traitNameContainerElement.classList.add("trait_name_container"); | ||||
| 
 | ||||
|     const traitNameTitleElement = document.createElement("h3"); | ||||
|     traitNameTitleElement.classList.add("trait_name"); | ||||
|     traitNameTitleElement.appendChild(document.createTextNode("Nom de la réaction\u00A0:")); | ||||
| 
 | ||||
|     traitNameContainerElement.appendChild(traitNameTitleElement); | ||||
| 
 | ||||
|     const traitNameInputElement = document.createElement("input"); | ||||
|     traitNameInputElement.classList.add("trait_name_input"); | ||||
|     traitNameInputElement.setAttribute("type", "text"); | ||||
|     traitNameInputElement.setAttribute("value", ""); | ||||
| 
 | ||||
|     traitNameContainerElement.appendChild(traitNameInputElement); | ||||
| 
 | ||||
|     traitElement.appendChild(traitNameContainerElement); | ||||
| 
 | ||||
|     const traitDescriptionElement = document.createElement("div"); | ||||
|     traitDescriptionElement.classList.add("trait_description_container"); | ||||
| 
 | ||||
|     const traitDescriptionTitleElement = document.createElement("h3"); | ||||
|     traitDescriptionTitleElement.classList.add("trait_description"); | ||||
|     traitDescriptionTitleElement.appendChild(document.createTextNode("Description de la réaction\u00A0:")); | ||||
| 
 | ||||
|     traitDescriptionElement.appendChild(traitDescriptionTitleElement); | ||||
| 
 | ||||
|     const traitDescriptionInputElement = document.createElement("input"); | ||||
|     traitDescriptionInputElement.classList.add("trait_description_input"); | ||||
|     traitDescriptionInputElement.setAttribute("type", "text"); | ||||
|     traitDescriptionInputElement.setAttribute("value", ""); | ||||
| 
 | ||||
|     traitDescriptionElement.appendChild(traitDescriptionInputElement); | ||||
| 
 | ||||
|     traitElement.appendChild(traitDescriptionElement); | ||||
| 
 | ||||
|     const buttonElement = document.createElement("button"); | ||||
|     buttonElement.classList.add("delete_trait_btn", "action_button", "short_color_transition"); | ||||
|     buttonElement.setAttribute("title", "Cliquez ici pour supprimer cette réaction"); | ||||
| 
 | ||||
|     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 réaction")); | ||||
|     buttonElement.addEventListener("click", () => deleteTrait(buttonElement)); | ||||
| 
 | ||||
|     traitElement.appendChild(buttonElement); | ||||
| 
 | ||||
|     const traitsElement = document.getElementById("traits"); | ||||
|     if (traitsElement === null) { | ||||
|         // No places element, this should never happen
 | ||||
|         // Do nothing in this case
 | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     traitsElement.appendChild(traitElement); | ||||
| } | ||||
| 
 | ||||
| function deleteTrait(traitDeletionButton) { | ||||
|     if (!confirm("Voulez-vous vraiement supprimer cette réaction ?")) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     const traitNode = traitDeletionButton.parentNode; | ||||
|     traitNode.parentNode.removeChild(traitNode); | ||||
| } | ||||
| 
 | ||||
| function saveChanges() { | ||||
|     const data = []; | ||||
| 
 | ||||
|     for (const traitElement of document.getElementsByClassName("trait")) { | ||||
|         const trait = {}; | ||||
|         trait["id"] = traitElement.getAttribute("data-id"); | ||||
|         trait["name"] = traitElement.querySelector(".trait_name_input").value; | ||||
|         trait["desc"] = traitElement.querySelector(".trait_description_input").value; | ||||
|         data.push(trait); | ||||
|     } | ||||
| 
 | ||||
|     makeAPIRequest("admin/setTraits", {"traits": data, "lang": "FR"}, {"content": "json"}).then(() => { | ||||
|         alert("Opération effectuée avec succès"); | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| function setListenersToTraitDeletionButtons() { | ||||
|     for (const deleteTraitButton of document.getElementsByClassName("delete_trait_btn")) { | ||||
|         deleteTraitButton.addEventListener("click", () => deleteTrait(deleteTraitButton)); | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| function setListenersToTraitAdditionButton() { | ||||
|     const addTraitButton = document.getElementById("add_trait"); | ||||
|     if (addTraitButton === null) { | ||||
|         // There is no add_place button, this should never happen
 | ||||
|         // Do nothing in this case
 | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     addTraitButton.addEventListener("click", addTrait); | ||||
| } | ||||
| 
 | ||||
| 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); | ||||
| } | ||||
| 
 | ||||
| setListenersToTraitDeletionButtons(); | ||||
| setListenersToTraitAdditionButton(); | ||||
| setListenersToSaveChangesButton(); | ||||
| @ -22,7 +22,12 @@ | ||||
|                 <label for="password" class="password_label">Mot de passe :</label> | ||||
|                 <input id="password" name="password" type="password" placeholder="Mot de passe" required="required" title="Saisissez votre mot de passe"> | ||||
|             </div> | ||||
|             <input type="submit" value="Connexion"> | ||||
|             <button id="login_input_button" class="action_button short_color_transition" type="submit" title="Cliquez ici pour vous connecter à l'interface d'administration de Truth Inquiry"> | ||||
|                 <svg class="action_icon short_color_transition" xmlns="http://www.w3.org/2000/svg" viewBox="0 96 960 960"> | ||||
|                     <path d="M483 959v-91h298V284H483v-92h298q36.125 0 63.562 27.206Q872 246.412 872 284v584q0 37.175-27.438 64.088Q817.125 959 781 959H483Zm-90-177-66-63 96-97H89v-91h332l-96-97 66-64 207 207-205 205Z"/> | ||||
|                 </svg> | ||||
|                 Connexion | ||||
|             </button> | ||||
|         </form> | ||||
|         {% if failed %} | ||||
|         <p class="invalid_password">Mot de passe invalide !</p> | ||||
|  | ||||
| @ -29,12 +29,12 @@ | ||||
|         <ul class="character_list"> | ||||
|             {%for npc in npcs%} | ||||
|             <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> | ||||
|             {%endfor%} | ||||
|         </ul> | ||||
|         <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"/> | ||||
|             </svg> | ||||
|             Nouveau personnage | ||||
|  | ||||
| @ -26,37 +26,41 @@ | ||||
|         <h1 class="page_title">Truth Inquiry - Interface d'administration</h1> | ||||
|         <h2 class="page_category">{{'Gestion' if npc.get('id') else 'Ajout'}} d'un personnage</h2> | ||||
|         <p class="page_description">Cliquez sur les champs pour éditer les informations. Dans les réponses aux questions lors de l'interrogation, utilisez « {NPC} » pour faire référence au nom d'un personnage et « {SALLE} » pour faire référence au nom d'une salle.</p> | ||||
|         <section> | ||||
|         <section id="character_info"> | ||||
|             <h2 class="section_title">Informations sur le personnage</h2> | ||||
|             <input id="npc_id" value="{{ npc.get('id') or ''}}" hidden="hidden"> | ||||
|             <input id="npc_id" value="{{ npc.get('id') or ''}}" type="hidden"> | ||||
|             <p class="info_item">Nom du personnage</p> | ||||
|             <input type="text" id="npc_name" value="{{ npc.get('name') or ''}}" title="Saisissez le nom du personnage" aria-label="Nom du personnage"> | ||||
|             <p class="info_item">Image du personnage</p> | ||||
|             <img class="character_image" alt="{{'Image du personnage' + (' ' + npc.get('name') if npc.get('name') else '')}}" src="{{'/static/images/no_photography_white.svg' if npc.get('img') == None else '/api/v1/getNpcImage?npcid=' + npc.get('img')|string}}"> | ||||
|         </section> | ||||
|          | ||||
|         <section id="reactions"> | ||||
|             <h2 class="section_title">Réactions</h2> | ||||
|              | ||||
|                 {%for reaction in npc.get("reactions") or []%} | ||||
|                 <div> | ||||
|                     <p> {{reaction.get('name')}} </p> | ||||
|                     <img src="{{reaction.get('url')}}" style="max-width: 100; max-height: 100px"> | ||||
|                     <input class="img_input", type="file" accept="image/png, image/jpeg" onchange="changeReaction(this)"> | ||||
|                     <input class="trait_id", type="hidden" value="{{reaction.get('trait_id')}}"> | ||||
|                     <button onclick="deleteReaction(this)">Delete reaction</button> | ||||
|                 </div> | ||||
|                 {%endfor%} | ||||
|         </section> | ||||
| 
 | ||||
|         <select id="reactions_to_add" onchange="addReaction(this)"> | ||||
|             <option value="" default></option> | ||||
|             {%for reaction_to_add in npc.get("reactions_to_add") or []%} | ||||
|             <option value="{{reaction_to_add.get('trait_id')}}">{{reaction_to_add.get('name')}}</option> | ||||
|             <h2 class="section_title">Images des réactions</h2> | ||||
|             {%for reaction in npc.get("reactions") or []%} | ||||
|             <section class="reaction"> | ||||
|                 <h3 class="reaction_name">{{reaction.get('name')}}</h3> | ||||
|                 <img class="reaction_image" alt="Image d'une réaction d'un personnage" src="{{reaction.get('url')}}"> | ||||
|                 <input class="img_input" type="file" accept="image/png, image/jpg, image/jpeg"> | ||||
|                 <input class="trait_id" type="hidden" value="{{reaction.get('trait_id')}}"> | ||||
|                 <button class="delete_image_reaction_btn action_button short_color_transition" title="Cliquez ici pour supprimer l'image de cette réaction"> | ||||
|                     <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 l'image de la réaction | ||||
|                 </button> | ||||
|             </section> | ||||
|             {%endfor%} | ||||
|         </select> | ||||
| 
 | ||||
|         <section> | ||||
|         </section> | ||||
|         <section id="add_reactions"> | ||||
|             <h2 class="section_title">Réactions à ajouter</h2> | ||||
|             <select id="add_reactions_selector"> | ||||
|                 <option value="" selected="selected">Sélectionnez une réaction à ajouter</option> | ||||
|                 {%for reaction_to_add in npc.get("reactions_to_add") or []%} | ||||
|                 <option value="{{reaction_to_add.get('trait_id')}}">{{reaction_to_add.get('name')}}</option> | ||||
|                 {%endfor%} | ||||
|             </select> | ||||
|         </section> | ||||
|         <section id="interrogation_answers"> | ||||
|             <h2 class="section_title">Réponses aux questions lors de l'interrogation</h2> | ||||
|             <div class="answer_groups"> | ||||
|                 {%for answer_type in npc.get("answers") or []%} | ||||
| @ -95,7 +99,8 @@ | ||||
|             <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 ajouter ou gérer un personnage.</p> | ||||
|                 <p class="alert_dialog_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 ajouter ou gérer un personnage.</p> | ||||
|                 <a class="alert_dialog_bottom_link short_color_transition" href="/api/v1/admin/logout" title="Cliquez ici pour vous déconnecter de l'interface d'administration du jeu">Se déconnecter de l'interface d'administration du jeu</a> | ||||
|             </dialog> | ||||
|         </noscript> | ||||
|         <script src="/static/js/api.js"></script> | ||||
|  | ||||
| @ -1,26 +1,65 @@ | ||||
| <!DOCTYPE html> | ||||
| <html lang="fr"> | ||||
|     <head> | ||||
|         <title>Places</title> | ||||
|         <meta charset="UTF-8"> | ||||
|         <link rel="stylesheet" href="admin_ui.css"> | ||||
|         <script src="/static/js/admin.js"></script> | ||||
|         <script src="/static/js/api.js"></script> | ||||
|         <title>Truth Inquiry - Gestion des lieux</title> | ||||
|         <link rel="stylesheet" href="/static/css/admin_ui.css"> | ||||
|         <link rel="stylesheet" href="/static/css/admin_ui_places.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> | ||||
|     <body> | ||||
|         <a href="/admin"> go Back </a> <br> | ||||
| 
 | ||||
| 
 | ||||
|         <section id="places"> | ||||
|         <header> | ||||
|             <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> | ||||
|             <a class="short_color_transition" href="/admin/places" title="Cliquez ici pour gérer les lieux du jeu">Gestion des lieux</a> | ||||
|             <a class="short_color_transition" href="/admin/traits" title="Cliquez ici pour gérer les réactions du jeu">Gestion des réactions</a> | ||||
|             <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> | ||||
|         </header> | ||||
|         <h1 class="page_title">Truth Inquiry - Interface d'administration</h1> | ||||
|         <h2 class="page_category">Gestion des lieux</h2> | ||||
|         <p class="page_description">Cliquez sur les champs pour éditer les lieux.</p> | ||||
|         <div id="places"> | ||||
|         {%for place in places%} | ||||
|             <section id="{{place['id']}}"> | ||||
|                 <input value="{{place['name']}}"> | ||||
|                 <button onclick="deleteInputPlaces(this)">Delete place</button> | ||||
|             </section> | ||||
|             <div id="{{place['id']}}" class="place"> | ||||
|                 <input class="place_input" type="text" value="{{place['name']}}"> | ||||
|                 <button class="delete_place_btn action_button short_color_transition" title="Cliquez ici pour supprimer ce lieu"> | ||||
|                     <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 le lieu | ||||
|                 </button> | ||||
|             </div> | ||||
|         {%endfor%} | ||||
|         </section> | ||||
|         <button onclick="addInputPlaces()">Add new</button> | ||||
|         <button onclick="saveFormPlaces()">Save changes</button> | ||||
| 
 | ||||
|         </div> | ||||
|         <button id="add_place" class="action_button short_color_transition" title="Cliquez ici pour ajouter un lieu"> | ||||
|             <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 un lieu | ||||
|         </button> | ||||
|         <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"> | ||||
|                 <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">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 lieux.</p> | ||||
|                 <a class="alert_dialog_bottom_link short_color_transition" href="/api/v1/admin/logout" title="Cliquez ici pour vous déconnecter de l'interface d'administration du jeu">Se déconnecter de l'interface d'administration du jeu</a> | ||||
|             </dialog> | ||||
|         </noscript> | ||||
|         <script src="/static/js/api.js"></script> | ||||
|         <script src="/static/js/admin_places.js"></script> | ||||
|     </body> | ||||
| </html> | ||||
|  | ||||
| @ -1,34 +1,70 @@ | ||||
| <!DOCTYPE html> | ||||
| <html lang="fr"> | ||||
|     <head> | ||||
|         <title>Admin Page</title> | ||||
|         <meta charset="UTF-8"> | ||||
|         <link rel="stylesheet" href="admin_ui.css"> | ||||
|         <script src="/static/js/admin.js"></script> | ||||
|         <script src="/static/js/api.js"></script> | ||||
|         <title>Truth Inquiry - Gestion des questions</title> | ||||
|         <link rel="stylesheet" href="/static/css/admin_ui.css"> | ||||
|         <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> | ||||
|     <body> | ||||
|      | ||||
|         <a href="/admin"> go Back </a> <br> | ||||
| 
 | ||||
|         <section id="allQuestions"> | ||||
|             {%for questionType in questions%} | ||||
|             <div class="questionType"> | ||||
|                 <section class="questionTypeContent"> | ||||
|                     {%for question in questionType["questions"]%} | ||||
|                     <section class="question"> | ||||
|                         <input value="{{question['text']}}"> | ||||
|                         <button onclick="deleteInputQuestions(this)">Delete question</button> | ||||
|                     </section> | ||||
|                     {%endfor%} | ||||
|                 </section> | ||||
|                 <button onclick="addInputQuestions(this)">Add new</button> | ||||
|         <header> | ||||
|             <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> | ||||
|             <a class="short_color_transition" href="/admin/places" title="Cliquez ici pour gérer les lieux du jeu">Gestion des lieux</a> | ||||
|             <a class="short_color_transition" href="/admin/traits" title="Cliquez ici pour gérer les réactions du jeu">Gestion des réactions</a> | ||||
|             <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> | ||||
|         </header> | ||||
|         <h1 class="page_title">Truth Inquiry - Interface d'administration</h1> | ||||
|         <h2 class="page_category">Gestion des questions</h2> | ||||
|         <p class="page_description">Cliquez sur les champs pour éditer les questions.</p> | ||||
|         {%for questionType in questions%} | ||||
|         <section class="question_type"> | ||||
|             <h3 class="question_type_title">Type de question {{loop.index0}}</h3> | ||||
|             <div class="question_type_list"> | ||||
|                 {%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> | ||||
|             {%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> | ||||
| 
 | ||||
|         <br> | ||||
| 
 | ||||
|         <button onclick="saveFormQuestions()"> Save changes </button> | ||||
|         {%endfor%} | ||||
|         <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"> | ||||
|                 <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">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> | ||||
|                 <a class="alert_dialog_bottom_link short_color_transition" href="/api/v1/admin/logout" title="Cliquez ici pour vous déconnecter de l'interface d'administration du jeu">Se déconnecter de l'interface d'administration du jeu</a> | ||||
|             </dialog> | ||||
|         </noscript> | ||||
|         <script src="/static/js/api.js"></script> | ||||
|         <script src="/static/js/admin_questions.js"></script> | ||||
|     </body> | ||||
| </html> | ||||
|  | ||||
| @ -1,29 +1,72 @@ | ||||
| <!DOCTYPE html> | ||||
| <html lang="fr"> | ||||
|     <head> | ||||
|         <title>Traits</title> | ||||
|         <meta charset="UTF-8"> | ||||
|         <link rel="stylesheet" href="admin_ui.css"> | ||||
|         <script src="/static/js/api.js"></script> | ||||
|         <script src="/static/js/admin.js"></script> | ||||
|         <title>Truth Inquiry - Gestion des réactions</title> | ||||
|         <link rel="stylesheet" href="/static/css/admin_ui.css"> | ||||
|         <link rel="stylesheet" href="/static/css/admin_ui_traits.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> | ||||
|     <body> | ||||
|         <a href="/admin"> go Back </a> <br> | ||||
| 
 | ||||
|         <section id="traits"> | ||||
|         <header> | ||||
|             <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> | ||||
|             <a class="short_color_transition" href="/admin/places" title="Cliquez ici pour gérer les lieux du jeu">Gestion des lieux</a> | ||||
|             <a class="short_color_transition" href="/admin/traits" title="Cliquez ici pour gérer les réactions du jeu">Gestion des réactions</a> | ||||
|             <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> | ||||
|         </header> | ||||
|         <h1 class="page_title">Truth Inquiry - Interface d'administration</h1> | ||||
|         <h2 class="page_category">Gestion des réactions</h2> | ||||
|         <p class="page_description">Cliquez sur les champs pour éditer les informations. Les images des émotions sont disponibles par personnage dans leur page de gestion dédiée.</p> | ||||
|         <div id="traits"> | ||||
|         {%for trait in traits%} | ||||
|             <section id="{{trait['id']}}"> | ||||
|                 <p> Name: </p> | ||||
|                 <input class="name_input" value="{{trait['name']}}"> | ||||
|                 <p> Description: </p> | ||||
|                 <input class="desc_input" value="{{trait['desc']}}"> | ||||
|                 <button onclick="deleteInputTraits(this)">Delete trait</button> | ||||
|             </section> | ||||
|             <div data-id="{{trait['id']}}" class="trait"> | ||||
|                 <div class="trait_name_container"> | ||||
|                     <h3 class="trait_name">Nom de la réaction :</h3> | ||||
|                     <input type="text" class="trait_name_input" value="{{trait['name']}}"> | ||||
|                 </div> | ||||
|                 <div class="trait_description_container"> | ||||
|                     <h3 class="trait_description">Description de la réaction :</h3> | ||||
|                     <input type="text" class="trait_description_input" value="{{trait['desc']}}"> | ||||
|                 </div> | ||||
|                 <button class="delete_trait_btn action_button short_color_transition"> | ||||
|                     <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 réaction | ||||
|                 </button> | ||||
|             </div> | ||||
|             {%endfor%} | ||||
|         </section> | ||||
|         <button onclick="addInputTraits()">Add new</button> | ||||
|         <button onclick="saveFormTraits()">Save changes</button> | ||||
| 
 | ||||
|         <p>Images are viewable in the npc views</p> | ||||
|         </div> | ||||
|         <button id="add_trait" class="action_button short_color_transition"> | ||||
|             <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 nouvelle réaction | ||||
|         </button> | ||||
|         <button id="save_changes" class="action_button short_color_transition"> | ||||
|             <svg class="action_icon short_color_transition" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 48 48"> | ||||
|                 <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">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 émotions.</p> | ||||
|                 <a class="alert_dialog_bottom_link short_color_transition" href="/api/v1/admin/logout" title="Cliquez ici pour vous déconnecter de l'interface d'administration du jeu">Se déconnecter de l'interface d'administration du jeu</a> | ||||
|             </dialog> | ||||
|         </noscript> | ||||
|         <script src="/static/js/api.js"></script> | ||||
|         <script src="/static/js/admin_traits.js"></script> | ||||
|     </body> | ||||
| </html> | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user