diff --git a/frontend/www/gallery.html b/frontend/www/gallery.html new file mode 100644 index 0000000000000000000000000000000000000000..64248fafe0458f71838666afd06c5174e5ee75d1 --- /dev/null +++ b/frontend/www/gallery.html @@ -0,0 +1,40 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Gallery</title> + <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous"> + + <script src="https://kit.fontawesome.com/0ce6c392ca.js" crossorigin="anonymous"></script> + <link rel="stylesheet" href="styles/style.css"> + <script src="scripts/navbar.js" type="text/javascript" defer></script> + </head> + <body> + <navbar-el></navbar-el> + + <div class="container"> + <h1 id="workout-title" class="mt-3"></h1> + <h3 id="workout-owner" class="mt-6"></h3> + <input type="button" class="btn btn-secondary" id="btn-back-workout" value=" Back to workout "> + + <div class="gallery-container"> + <div class="main-img"> + <img src="" id="current"> + </div> + + <h3 id="no-images-text" class="mt-6">This workout has no images.</h3> + <div class="imgs" id="img-collection-container"> + <div id="img-collection"></div> + <div id="img-collection-delete"></div> + </div> + </div> + </div> + + <script src="scripts/scripts.js"></script> + <script src="scripts/defaults.js"></script> + <script src="scripts/gallery.js"></script> + <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-ygbV9kiqUc6oa4msXn9868pTtWMgiQaeYH7/t7LECLbyPA2x65Kgf80OJFdroafW" crossorigin="anonymous"></script> + </body> + +</html> \ No newline at end of file diff --git a/frontend/www/scripts/gallery.js b/frontend/www/scripts/gallery.js new file mode 100644 index 0000000000000000000000000000000000000000..f9c07b449947470c8df29c8f51894758cf38c025 --- /dev/null +++ b/frontend/www/scripts/gallery.js @@ -0,0 +1,131 @@ +let goBackButton; +let submitNewFileButton; + +async function retrieveWorkoutImages(id) { + let workoutData = null; + let response = await sendRequest("GET", `${HOST}/api/workouts/${id}/`); + if (!response.ok) { + let data = await response.json(); + let alert = createAlert("Could not retrieve workout data!", data); + document.body.prepend(alert); + } else { + workoutData = await response.json(); + + document.getElementById("workout-title").innerHTML = "Workout name: " + workoutData["name"]; + document.getElementById("workout-owner").innerHTML = "Owner: " + workoutData["owner_username"]; + + let hasNoImages = workoutData.files.length == 0; + let noImageText = document.querySelector("#no-images-text"); + + if(hasNoImages){ + noImageText.classList.remove("hide"); + return; + } + + noImageText.classList.add("hide"); + + + let filesDiv = document.getElementById("img-collection"); + let filesDeleteDiv = document.getElementById("img-collection-delete"); + + const currentImageFileElement = document.querySelector("#current"); + let isFirstImg = true; + + let fileCounter = 0; + + for (let file of workoutData.files) { + let a = document.createElement("a"); + a.href = file.file; + let pathArray = file.file.split("/"); + a.text = pathArray[pathArray.length - 1]; + a.className = "me-2"; + + + + let isImage = ["jpg", "png", "gif", "jpeg", "JPG", "PNG", "GIF", "JPEG"].includes(a.text.split(".")[1]); + + if(isImage){ + let deleteImgButton = document.createElement("input"); + deleteImgButton.type = "button"; + deleteImgButton.className = "btn btn-close"; + deleteImgButton.id = file.url.split("/")[file.url.split("/").length - 2]; + deleteImgButton.addEventListener('click', () => handleDeleteImgClick(deleteImgButton.id, "DELETE", `Could not delete workout ${deleteImgButton.id}!`, HOST, ["jpg", "png", "gif", "jpeg", "JPG", "PNG", "GIF", "JPEG"])); + filesDeleteDiv.appendChild(deleteImgButton); + + let img = document.createElement("img"); + img.src = file.file; + + filesDiv.appendChild(img); + deleteImgButton.style.left = `${(fileCounter % 4) * 191}px`; + deleteImgButton.style.top = `${Math.floor(fileCounter / 4) * 105}px`; + + if(isFirstImg){ + currentImageFileElement.src = file.file; + isFirstImg = false; + } + fileCounter++; + } + } + + const otherImageFileElements = document.querySelectorAll(".imgs img"); + const selectedOpacity = 0.6; + otherImageFileElements[0].style.opacity = selectedOpacity; + + otherImageFileElements.forEach((imageFileElement) => imageFileElement.addEventListener("click", (event) => { + //Changes the main image + currentImageFileElement.src = event.target.src; + + //Adds the fade animation + currentImageFileElement.classList.add('fade-in') + setTimeout(() => currentImageFileElement.classList.remove('fade-in'), 500); + + //Sets the opacity of the selected image to 0.4 + otherImageFileElements.forEach((imageFileElement) => imageFileElement.style.opacity = 1) + event.target.style.opacity = selectedOpacity; + })) + + } + return workoutData; +} + +async function validateImgFileType(id, host_variable, acceptedFileTypes) { + let file = await sendRequest("GET", `${host_variable}/api/workout-files/${id}/`); + let fileData = await file.json(); + let fileType = fileData.file.split("/")[fileData.file.split("/").length - 1].split(".")[1]; + + return acceptedFileTypes.includes(fileType); +} + +async function handleDeleteImgClick (id, http_keyword, fail_alert_text, host_variable, acceptedFileTypes) { + + if(validateImgFileType(id, host_variable, acceptedFileTypes, )){ + return + } + + let response = await sendRequest(http_keyword, `${host_variable}/api/workout-files/${id}/`); + + if (!response.ok) { + let data = await response.json(); + let alert = createAlert(fail_alert_text, data); + document.body.prepend(alert); + } else { + location.reload(); + } +} + +function handleGoBackToWorkoutClick() { + const urlParams = new URLSearchParams(window.location.search); + const id = urlParams.get('id'); + window.location.replace(`workout.html?id=${id}`); +} + +window.addEventListener("DOMContentLoaded", async () => { + + goBackButton = document.querySelector("#btn-back-workout"); + goBackButton.addEventListener('click', handleGoBackToWorkoutClick); + + const urlParams = new URLSearchParams(window.location.search); + const id = urlParams.get('id'); + let workoutData = await retrieveWorkoutImages(id); + +}); \ No newline at end of file diff --git a/frontend/www/scripts/workout.js b/frontend/www/scripts/workout.js index c02f4ae4cfb0ad90fb94e57c208f4f13b638a77f..20d21b1878f666b8a205df28a57c1cde8ce8a4d7 100644 --- a/frontend/www/scripts/workout.js +++ b/frontend/www/scripts/workout.js @@ -3,6 +3,7 @@ let okWorkoutButton; let deleteWorkoutButton; let editWorkoutButton; let postCommentButton; +let galleryButton; async function retrieveWorkout(id) { let workoutData = null; @@ -41,19 +42,9 @@ async function retrieveWorkout(id) { let pathArray = file.file.split("/"); a.text = pathArray[pathArray.length - 1]; a.className = "me-2"; - let img - let isImage = ["jpg", "png", "gif", "jpeg", "JPG", "PNG", "GIF", "JPEG"].includes(a.text.split(".")[1]); - if(isImage){ - img = document.createElement("img"); - img.src = file.file; - img.width = "500"; - } filesDiv.appendChild(a); - if(isImage){ - filesDiv.appendChild(img); - } } // create exercises @@ -126,6 +117,7 @@ function handleEditWorkoutButtonClick() { document.querySelector("#inputOwner").readOnly = true; // owner field should still be readonly editWorkoutButton.className += " hide"; + galleryButton.className += " hide"; okWorkoutButton.className = okWorkoutButton.className.replace(" hide", ""); cancelWorkoutButton.className = cancelWorkoutButton.className.replace(" hide", ""); deleteWorkoutButton.className = deleteWorkoutButton.className.replace(" hide", ""); @@ -136,6 +128,13 @@ function handleEditWorkoutButtonClick() { } +function handleGalleryButtonClick() { + const urlParams = new URLSearchParams(window.location.search); + + const id = urlParams.get('id'); + window.location.replace(`gallery.html?id=${id}`); +} + async function deleteWorkout(id) { let response = await sendRequest("DELETE", `${HOST}/api/workouts/${id}/`); if (!response.ok) { @@ -321,6 +320,7 @@ window.addEventListener("DOMContentLoaded", async () => { okWorkoutButton = document.querySelector("#btn-ok-workout"); deleteWorkoutButton = document.querySelector("#btn-delete-workout"); editWorkoutButton = document.querySelector("#btn-edit-workout"); + galleryButton = document.querySelector("#btn-gallery-workout"); let postCommentButton = document.querySelector("#post-comment"); let divCommentRow = document.querySelector("#div-comment-row"); let buttonAddExercise = document.querySelector("#btn-add-exercise"); @@ -329,6 +329,9 @@ window.addEventListener("DOMContentLoaded", async () => { buttonAddExercise.addEventListener("click", createBlankExercise); buttonRemoveExercise.addEventListener("click", removeExercise); + galleryButton.addEventListener("click", handleGalleryButtonClick); + + const urlParams = new URLSearchParams(window.location.search); let currentUser = await getCurrentUser(); @@ -356,6 +359,8 @@ window.addEventListener("DOMContentLoaded", async () => { cancelWorkoutButton.className = cancelWorkoutButton.className.replace(" hide", ""); buttonAddExercise.className = buttonAddExercise.className.replace(" hide", ""); buttonRemoveExercise.className = buttonRemoveExercise.className.replace(" hide", ""); + galleryButton.className += " hide"; + okWorkoutButton.addEventListener("click", async () => await createWorkout()); cancelWorkoutButton.addEventListener("click", handleCancelDuringWorkoutCreate); diff --git a/frontend/www/styles/style.css b/frontend/www/styles/style.css index 066705ce965bb162c61ebaf65ff77b9a0824eaf3..c3d26399d17c7a095afcca75526ddf3320ff9817 100644 --- a/frontend/www/styles/style.css +++ b/frontend/www/styles/style.css @@ -62,3 +62,57 @@ .link-block { display: block; } + + +.gallery-container { + max-width: 760px; + margin: auto; + border: #fff solid 3px; + background: #fff; +} + +.main-img img, .imgs img { + width: 100%; + max-height: 400px; +} + +.imgs > div{ + display: grid; + grid-template-columns: repeat(4, 1fr); + grid-gap: 5px; + border-top: #fff solid 20px; + position: relative; +} + +#img-collection img { + height: 100px; + width: 186px; +} + +#img-collection-delete input { + height: 20px; + width: 20px; + background-color: gray; + position: absolute; +} + +#img-collection { + z-index: 1; + position: absolute; +} + +#img-collection-delete { + z-index: 2; + position: absolute; +} + +@keyframes fadeIn { + to { + opacity: 1; + } +} + +.fade-in { + opacity: 0; + animation: fadeIn 0.5s ease-in 1 forwards ; +} \ No newline at end of file diff --git a/frontend/www/workout.html b/frontend/www/workout.html index 73747232adaea6ef4944cdd0fbec8693ca6b4a76..e6282718c086bd877b684b79bb2d1e59bd51acb5 100644 --- a/frontend/www/workout.html +++ b/frontend/www/workout.html @@ -57,12 +57,18 @@ </div> <div class="col-lg-6"> </div> + <div class="col-lg-6"> <input type="button" class="btn btn-primary hide" id="btn-ok-workout" value=" OK "> <input type="button" class="btn btn-primary hide" id="btn-edit-workout" value=" Edit "> <input type="button" class="btn btn-secondary hide" id="btn-cancel-workout" value="Cancel"> <input type="button" class="btn btn-danger float-end hide" id="btn-delete-workout" value="Delete"> </div> + + <div class="col-lg-6"> + <input type="button" class="btn btn-info" id="btn-gallery-workout" value=" Go to gallery "> + </div> + <div class="col-lg-6"></div> <div class="col-lg-12"> <h3 class="mt-3">Exercises</h3>