Skip to content
Snippets Groups Projects
Forked from Åsmund Haugse / tdt4242-base
29 commits behind, 37 commits ahead of the upstream repository.
workout.js 16.67 KiB
let cancelWorkoutButton;
let okWorkoutButton;
let deleteWorkoutButton;
let editWorkoutButton;
let exportWorkoutButton;
let postCommentButton;

async function retrieveWorkout(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();
        let form = document.querySelector("#form-workout");
        let formData = new FormData(form);

        for (let key of formData.keys()) {
            let selector = `input[name="${key}"], textarea[name="${key}"]`;
            let input = form.querySelector(selector);
            let newVal = workoutData[key];
            if (key == "date") {
                // Creating a valid datetime-local string with the correct local time
                let date = new Date(newVal);
                date = new Date(date.getTime() - (date.getTimezoneOffset() * 60 * 1000)).toISOString(); // get ISO format for local time
                newVal = date.substring(0, newVal.length - 1);    // remove Z (since this is a local time, not UTC)
            }
            if (key != "files") {
                input.value = newVal;
            }
        }

        let input = form.querySelector("select:disabled");
        input.value = workoutData["visibility"];
        // files
        let filesDiv = document.querySelector("#uploaded-files");
        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";
            filesDiv.appendChild(a);
        }

        // create exercises

        // fetch exercise types
        let exerciseTypeResponse = await sendRequest("GET", `${HOST}/api/exercises/`);
        let exerciseTypes = await exerciseTypeResponse.json();

        //TODO: This should be in its own method.
        for (let i = 0; i < workoutData.exercise_instances.length; i++) {
            let templateExercise = document.querySelector("#template-exercise");
            let divExerciseContainer = templateExercise.content.firstElementChild.cloneNode(true);

            let exerciseTypeLabel = divExerciseContainer.querySelector('.exercise-type');
            exerciseTypeLabel.for = `inputExerciseType${i}`;

            let exerciseTypeSelect = divExerciseContainer.querySelector("select");
            exerciseTypeSelect.id = `inputExerciseType${i}`;
            exerciseTypeSelect.disabled = true;

            let splitUrl = workoutData.exercise_instances[i].exercise.split("/");
            let currentExerciseTypeId = splitUrl[splitUrl.length - 2];
            let currentExerciseType = "";

            for (let j = 0; j < exerciseTypes.count; j++) {
                let option = document.createElement("option");
                option.value = exerciseTypes.results[j].id;
                if (currentExerciseTypeId == exerciseTypes.results[j].id) {
                    currentExerciseType = exerciseTypes.results[j];
                }
                option.innerText = exerciseTypes.results[j].name;
                exerciseTypeSelect.append(option);
            }

            exerciseTypeSelect.value = currentExerciseType.id;

            let exerciseSetLabel = divExerciseContainer.querySelector('.exercise-sets');
            exerciseSetLabel.for = `inputSets${i}`;

            let exerciseSetInput = divExerciseContainer.querySelector("input[name='sets']");
            exerciseSetInput.id = `inputSets${i}`;
            exerciseSetInput.value = workoutData.exercise_instances[i].sets;
            exerciseSetInput.readOnly = true;

            let exerciseNumberLabel = divExerciseContainer.querySelector('.exercise-number');
            exerciseNumberLabel.for = "for", `inputNumber${i}`;
            exerciseNumberLabel.innerText = currentExerciseType.unit;

            let exerciseNumberInput = divExerciseContainer.querySelector("input[name='number']");
            exerciseNumberInput.id = `inputNumber${i}`;
            exerciseNumberInput.value = workoutData.exercise_instances[i].number;
            exerciseNumberInput.readOnly = true;

            let exercisesDiv = document.querySelector("#div-exercises");
            exercisesDiv.appendChild(divExerciseContainer);
        }
    }
    return workoutData;
}

function handleCancelDuringWorkoutEdit() {
    location.reload();
}

function handleEditWorkoutButtonClick() {
    let addExerciseButton = document.querySelector("#btn-add-exercise");
    let removeExerciseButton = document.querySelector("#btn-remove-exercise");

    setReadOnly(false, "#form-workout");
    document.querySelector("#inputOwner").readOnly = true;  // owner field should still be readonly 

    editWorkoutButton.className += " hide";
    exportWorkoutButton.className += " hide";
    okWorkoutButton.className = okWorkoutButton.className.replace(" hide", "");
    cancelWorkoutButton.className = cancelWorkoutButton.className.replace(" hide", "");
    deleteWorkoutButton.className = deleteWorkoutButton.className.replace(" hide", "");
    addExerciseButton.className = addExerciseButton.className.replace(" hide", "");
    removeExerciseButton.className = removeExerciseButton.className.replace(" hide", "");

    cancelWorkoutButton.addEventListener("click", handleCancelDuringWorkoutEdit);

}

//Taken from github: https://gist.github.com/dannypule/48418b4cd8223104c6c92e3016fc0f61
function handleExportToCalendarClick(workoutData) {

    const headers = {
        subject: "Subject",
        startDate: "Start date",
        startTime: "Start time",
        description: "Description"
    }

    const dataFormatted = []
    const startTime = new Date(workoutData.date).toLocaleTimeString("en-us")
    const startDate = new Date(workoutData.date).toLocaleString('en-us', {
        year: 'numeric',
        month: '2-digit',
        day: '2-digit'
    }).replace(/(\d+)\/(\d+)\/(\d+)/, '$1/$2/$3')


    dataFormatted.push({
        subject: workoutData.name,
        startDate: startDate,
        startTime: startTime,
        description: workoutData.notes
    })


    console.log(dataFormatted)

    exportCSVFile(headers, dataFormatted, "event")
}

//Taken from github: https://gist.github.com/dannypule/48418b4cd8223104c6c92e3016fc0f61
function convertToCSV(objArray) {
    var array = typeof objArray != 'object' ? JSON.parse(objArray) : objArray;
    var str = '';

    for (var i = 0; i < array.length; i++) {
        var line = '';
        for (var index in array[i]) {
            if (line != '') line += ','

            line += array[i][index];
        }

        str += line + '\r\n';
    }

    return str;
}

//Taken from github: https://gist.github.com/dannypule/48418b4cd8223104c6c92e3016fc0f61
function exportCSVFile(headers, items, fileTitle) {

    console.log(items, headers)
    if (headers) {
        items.unshift(headers);
    }

    // Convert Object to JSON
    var jsonObject = JSON.stringify(items);

    var csv = this.convertToCSV(jsonObject);

    var exportedFilenmae = fileTitle + '.csv' || 'export.csv';

    var blob = new Blob([csv], {type: 'text/csv;charset=utf-8;'});
    if (navigator.msSaveBlob) { // IE 10+
        navigator.msSaveBlob(blob, exportedFilenmae);
    } else {
        var link = document.createElement("a");
        if (link.download !== undefined) { // feature detection
            // Browsers that support HTML5 download attribute
            var url = URL.createObjectURL(blob);
            link.setAttribute("href", url);
            link.setAttribute("download", exportedFilenmae);
            link.style.visibility = 'hidden';
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
        }
    }
}

async function deleteWorkout(id) {
    let response = await sendRequest("DELETE", `${HOST}/api/workouts/${id}/`);
    if (!response.ok) {
        let data = await response.json();
        let alert = createAlert(`Could not delete workout ${id}!`, data);
        document.body.prepend(alert);
    } else {
        window.location.replace("workouts.html");
    }
}

async function updateWorkout(id) {
    let submitForm = generateWorkoutForm();

    let response = await sendRequest("PUT", `${HOST}/api/workouts/${id}/`, submitForm, "");
    if (!response.ok) {
        let data = await response.json();
        let alert = createAlert("Could not update workout!", data);
        document.body.prepend(alert);
    } else {
        location.reload();
    }
}

function generateWorkoutForm() {
    let form = document.querySelector("#form-workout");

    let formData = new FormData(form);
    let submitForm = new FormData();

    submitForm.append("name", formData.get('name'));
    let date = new Date(formData.get('date')).toISOString();
    submitForm.append("date", date);
    submitForm.append("notes", formData.get("notes"));
    submitForm.append("visibility", formData.get("visibility"));

    // adding exercise instances
    let exerciseInstances = [];
    let exerciseInstancesTypes = formData.getAll("type");
    let exerciseInstancesSets = formData.getAll("sets");
    let exerciseInstancesNumbers = formData.getAll("number");
    for (let i = 0; i < exerciseInstancesTypes.length; i++) {
        exerciseInstances.push({
            exercise: `${HOST}/api/exercises/${exerciseInstancesTypes[i]}/`,
            number: exerciseInstancesNumbers[i],
            sets: exerciseInstancesSets[i]
        });
    }

    submitForm.append("exercise_instances", JSON.stringify(exerciseInstances));
    // adding files
    for (let file of formData.getAll("files")) {
        submitForm.append("files", file);
    }
    return submitForm;
}

async function createWorkout() {
    let submitForm = generateWorkoutForm();

    let response = await sendRequest("POST", `${HOST}/api/workouts/`, submitForm, "");

    if (response.ok) {
        window.location.replace("workouts.html");
    } else {
        let data = await response.json();
        let alert = createAlert("Could not create new workout!", data);
        document.body.prepend(alert);
    }
}

function handleCancelDuringWorkoutCreate() {
    window.location.replace("workouts.html");
}

async function createBlankExercise() {
    let form = document.querySelector("#form-workout");

    let exerciseTypeResponse = await sendRequest("GET", `${HOST}/api/exercises/`);
    let exerciseTypes = await exerciseTypeResponse.json();

    let exerciseTemplate = document.querySelector("#template-exercise");
    let divExerciseContainer = exerciseTemplate.content.firstElementChild.cloneNode(true);
    let exerciseTypeSelect = divExerciseContainer.querySelector("select");

    for (let i = 0; i < exerciseTypes.count; i++) {
        let option = document.createElement("option");
        option.value = exerciseTypes.results[i].id;
        option.innerText = exerciseTypes.results[i].name;
        exerciseTypeSelect.append(option);
    }

    let currentExerciseType = exerciseTypes.results[0];
    exerciseTypeSelect.value = currentExerciseType.name;

    let divExercises = document.querySelector("#div-exercises");
    divExercises.appendChild(divExerciseContainer);
}

function removeExercise(event) {
    let divExerciseContainers = document.querySelectorAll(".div-exercise-container");
    if (divExerciseContainers && divExerciseContainers.length > 0) {
        divExerciseContainers[divExerciseContainers.length - 1].remove();
    }
}

function addComment(author, text, date, append) {
    /* Taken from https://www.bootdey.com/snippets/view/Simple-Comment-panel#css*/
    let commentList = document.querySelector("#comment-list");
    let listElement = document.createElement("li");
    listElement.className = "media";
    let commentBody = document.createElement("div");
    commentBody.className = "media-body";
    let dateSpan = document.createElement("span");
    dateSpan.className = "text-muted pull-right me-1";
    let smallText = document.createElement("small");
    smallText.className = "text-muted";

    if (date != "Now") {
        let localDate = new Date(date);
        smallText.innerText = localDate.toLocaleString();
    } else {
        smallText.innerText = date;
    }

    dateSpan.appendChild(smallText);
    commentBody.appendChild(dateSpan);

    let strong = document.createElement("strong");
    strong.className = "text-success";
    strong.innerText = author;
    commentBody.appendChild(strong);
    let p = document.createElement("p");
    p.innerHTML = text;

    commentBody.appendChild(strong);
    commentBody.appendChild(p);
    listElement.appendChild(commentBody);

    if (append) {
        commentList.append(listElement);
    } else {
        commentList.prepend(listElement);
    }

}

async function createComment(workoutid) {
    let commentArea = document.querySelector("#comment-area");
    let content = commentArea.value;
    let body = {workout: `${HOST}/api/workouts/${workoutid}/`, content: content};

    let response = await sendRequest("POST", `${HOST}/api/comments/`, body);
    if (response.ok) {
        addComment(sessionStorage.getItem("username"), content, "Now", false);
    } else {
        let data = await response.json();
        let alert = createAlert("Failed to create comment!", data);
        document.body.prepend(alert);
    }
}

async function retrieveComments(workoutid) {
    let response = await sendRequest("GET", `${HOST}/api/comments/`);
    if (!response.ok) {
        let data = await response.json();
        let alert = createAlert("Could not retrieve comments!", data);
        document.body.prepend(alert);
    } else {
        let data = await response.json();
        let comments = data.results;
        for (let comment of comments) {
            let splitArray = comment.workout.split("/");
            if (splitArray[splitArray.length - 2] == workoutid) {
                addComment(comment.owner, comment.content, comment.timestamp, true);
            }
        }
    }
}

window.addEventListener("DOMContentLoaded", async () => {
    cancelWorkoutButton = document.querySelector("#btn-cancel-workout");
    okWorkoutButton = document.querySelector("#btn-ok-workout");
    deleteWorkoutButton = document.querySelector("#btn-delete-workout");
    editWorkoutButton = document.querySelector("#btn-edit-workout");
    exportWorkoutButton = document.querySelector("#btn-export-workout");
    let postCommentButton = document.querySelector("#post-comment");
    let divCommentRow = document.querySelector("#div-comment-row");
    let buttonAddExercise = document.querySelector("#btn-add-exercise");
    let buttonRemoveExercise = document.querySelector("#btn-remove-exercise");

    buttonAddExercise.addEventListener("click", createBlankExercise);
    buttonRemoveExercise.addEventListener("click", removeExercise);

    const urlParams = new URLSearchParams(window.location.search);
    let currentUser = await getCurrentUser();

    if (urlParams.has('id')) {
        const id = urlParams.get('id');
        let workoutData = await retrieveWorkout(id);
        await retrieveComments(id);

        if (workoutData["owner"] == currentUser.url) {
            editWorkoutButton.classList.remove("hide");
            exportWorkoutButton.classList.remove("hide");
            editWorkoutButton.addEventListener("click", handleEditWorkoutButtonClick);
            exportWorkoutButton.addEventListener("click", ((workoutData) => handleExportToCalendarClick(workoutData)).bind(undefined, workoutData));
            deleteWorkoutButton.addEventListener("click", (async (id) => await deleteWorkout(id)).bind(undefined, id));
            okWorkoutButton.addEventListener("click", (async (id) => await updateWorkout(id)).bind(undefined, id));
            postCommentButton.addEventListener("click", (async (id) => await createComment(id)).bind(undefined, id));
            divCommentRow.className = divCommentRow.className.replace(" hide", "");
        }
    } else {
        await createBlankExercise();
        let ownerInput = document.querySelector("#inputOwner");
        ownerInput.value = currentUser.username;
        setReadOnly(false, "#form-workout");
        ownerInput.readOnly = !ownerInput.readOnly;

        okWorkoutButton.className = okWorkoutButton.className.replace(" hide", "");
        cancelWorkoutButton.className = cancelWorkoutButton.className.replace(" hide", "");
        buttonAddExercise.className = buttonAddExercise.className.replace(" hide", "");
        buttonRemoveExercise.className = buttonRemoveExercise.className.replace(" hide", "");

        okWorkoutButton.addEventListener("click", async () => await createWorkout());
        cancelWorkoutButton.addEventListener("click", handleCancelDuringWorkoutCreate);
        divCommentRow.className += " hide";
    }

});