Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • aasmuha/tdt4242-base
  • sebastvi/tdt4242-base
  • haavafar/tdt4242-base
  • tdt4242-spring-2021-t17/tdt4242-base
  • tmwang/tdt4242-base
  • tdt4242-group-t4/tdt4242-base
  • andstorh/tdt4242-base
  • reaas/tdt4242-base
  • andrerim/tdt4242-base
  • kristohh/tdt4242-base
  • andreajj/tdt4242-base
  • vegarms/tdt4242-base
  • andrend/tdt4242-base
  • haavarhu/tdt4242-base
  • mathilah/tdt4242-base
  • tmmothe/tdt4242-base
  • miriams/tdt4242-base
  • harkamas/tdt4242-base
  • chrisclo/tdt-4242-group-13
  • estherv/tdt4242-base
  • mariueng/tdt-4242-secfit
  • haakogun/secfit-haakon
  • haakonrj/tdt-4242-35
  • tdt4242-group-5/secfit
  • haakonjf/tdt-4242-gruppe6
  • nikolard/tdt-4242-sec-fit
  • tdt4242-gr24/tdt4242-base
  • abdulfaa/tdt4242-base
  • jmjohnse/tdt-4242-group-37-use
  • owengg/tdt-4242-base-owen-forked
  • ktsiow/tdt4242-base
  • olechrib/tdt4242-group16
  • eytan/tdt-4242-base-group-33
33 results
Show changes
Showing
with 759 additions and 16 deletions
# Generated by Django 3.1 on 2021-10-20 07:50
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('workouts', '0003_rememberme'),
]
operations = [
migrations.AddField(
model_name='exercise',
name='calories',
field=models.IntegerField(default=0),
),
migrations.AddField(
model_name='exercise',
name='duration',
field=models.IntegerField(default=0),
),
migrations.AddField(
model_name='exercise',
name='muscleGroup',
field=models.TextField(default='Legs'),
),
]
......@@ -76,11 +76,17 @@ class Exercise(models.Model):
Attributes:
name: Name of the exercise type
description: Description of the exercise type
duration: Duration of one unit of the exercise
calories: Calories spent per minute
muscleGroup: What major muscle group is used in the exercise
unit: Name of the unit for the exercise type (e.g., reps, seconds)
"""
name = models.CharField(max_length=100)
description = models.TextField()
duration = models.IntegerField(default=0)
calories = models.IntegerField(default=0)
muscleGroup = models.TextField(default="Legs")
unit = models.CharField(max_length=50)
def __str__(self):
......
......@@ -201,7 +201,7 @@ class WorkoutSerializer(serializers.HyperlinkedModelSerializer):
class ExerciseSerializer(serializers.HyperlinkedModelSerializer):
"""Serializer for an Exercise. Hyperlinks are used for relationships by default.
Serialized fields: url, id, name, description, unit, instances
Serialized fields: url, id, name, description, duration, calories, muscle group, unit, instances
Attributes:
instances: Associated exercise instances with this Exercise type. Hyperlinks.
......@@ -213,7 +213,7 @@ class ExerciseSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Exercise
fields = ["url", "id", "name", "description", "unit", "instances"]
fields = ["url", "id", "name", "description", "duration", "calories", "muscleGroup", "unit", "instances"]
class RememberMeSerializer(serializers.HyperlinkedModelSerializer):
......
web: cd frontend && cordova run browser --release --port=$PORT
\ No newline at end of file
......@@ -31,10 +31,32 @@
</div>
<div class="col-lg-6"></div>
<div class="col-lg-6">
<label for="inputUnit" class="form-label">Unit</label>
<label for="inputUnit" class="form-label">Unit (Reps, time, etc.)</label>
<input type="text" class="form-control" id="inputUnit" name="unit" readonly>
</div>
<div class="col-lg-6"></div>
<div class="col-lg-6">
<label for="inputDuration" class="form-label"> Duration(Expects a positive integer)</label>
<input type="number" class="form-control" id="inputDuration" name="duration" readonly></input>
</div>
<div class="col-lg-6"></div>
<div class="col-lg-6">
<label for="inputCalories" class="form-label"> Calories burned (Expects a positive integer)</label>
<input type="number" class="form-control" id="inputCalories" name="calories" readonly></input>
</div>
<div class="col-lg-6"></div>
<div class="col-lg-6">
<label for="inputMuscles" class="form-label">Muscle group used</label>
<select class="form-select" name="muscleGroup" disabled="true">
<option value="Legs">Legs</option>
<option value="Chest">Chest</option>
<option value="Back">Back</option>
<option value="Arms">Arms</option>
<option value="Abdomen">Abdominals</option>
<option value="Shoulders">Shoulders</option>
</select>
</div>
<div class="col-lg-6"></div>
<div class="col-lg-6">
<input type="button" class="btn btn-primary hide" id="btn-ok-exercise" value=" OK ">
<input type="button" class="btn btn-primary" id="btn-edit-exercise" value=" Edit ">
......
<!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
......@@ -17,7 +17,7 @@
<div class="row mt-3">
<div class="col-lg text-center">
<h2 class="mt-3">Welcome to SecFit!</h2>
<p>SecFit (coming from "Secure" and "Fitness") is the most secure fitness logging app on the net.
<p>SecFit (coming from "SuperSecure" and "Fitness") is the most secure fitness logging app on the net.
You can conveniently log a workout using either our website or our app. You can also view and comment on others'
workouts!
</p>
......
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Meal</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">
<div class="row">
<div class="col-lg">
<h3 class="mt-3">View or edit your registered meal</h3>
</div>
</div>
<form class="row g-3 mb-4" id="form-meal">
<div class="col-lg-6 ">
<label for="inputName" class="form-label">Name</label>
<input type="text" class="form-control" id="inputName" name="name" readonly>
</div>
<div class="col-lg-6"></div>
<div class="col-lg-6">
<label for="inputDateTime" class="form-label">Date/Time</label>
<input type="datetime-local" class="form-control" id="inputDateTime" name="date" readonly>
</div>
<div class="col-lg-6"></div>
<div class="col-lg-6">
<label for="inputOwner" class="form-label">Owner</label>
<input type="text" class="form-control" id="inputOwner" name="owner_username" readonly>
</div>
<div class="col-lg-6"></div>
<div class="col-lg-6">
<label for="inputNotes" class="form-label">Notes</label>
<textarea class="form-control" id="inputNotes" name="notes" readonly></textarea>
</div>
<div class="col-lg-6"></div>
<div class="col-lg-6">
<label for="inputCalories" class="form-label">Calories</label>
<input type="number" class="form-control" id="inputCalories" name="calories" readonly></input>
</div>
<div class="col-lg-6"></div>
<div class="col-lg-6">
<div class="input-group">
<input type="file" class="form-control" id="customFile" name="files" multiple disabled>
</div>
<div id="uploaded-files" class="ms-1 mt-2">
</div>
</div>
<div class="col-lg-6">
</div>
<div class="col-lg-6">
<input type="button" class="btn btn-primary hide" id="btn-ok-meal" value=" OK ">
<input type="button" class="btn btn-primary hide" id="btn-edit-meal" value=" Edit ">
<input type="button" class="btn btn-secondary hide" id="btn-cancel-meal" value="Cancel">
<input type="button" class="btn btn-danger float-end hide" id="btn-delete-meal" value="Delete">
</div>
<div class="col-lg-6"></div>
</form>
</div>
<script src="scripts/defaults.js"></script>
<script src="scripts/scripts.js"></script>
<script src="scripts/meal.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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Meals</title>
<link rel="stylesheet" href="styles/style.css">
<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>
<script src="scripts/navbar.js" type="text/javascript" defer></script>
</head>
<body>
<navbar-el></navbar-el>
<div class="container">
<div class="row">
<div class="col-lg text-center">
<h3 class="mt-5">View meals</h3>
<p>Here you can view your logged meals. Click on a meal to view its details.</p>
<input type="button" class="btn btn-success" id="btn-create-meal" value="Log new meal">
</div>
</div>
<div class="row">
<div class="col-lg text-center">
<div class="mt-1">Sort by: <a href="?ordering=date">Date</a> <a href="?ordering=name">Name</a>
<br>Currently sorting by: <span id="current-sort"></span>
</div>
<div class="list-group mt-1" id="div-content"></div>
</div>
</div>
</div>
<template id="template-meal">
<a class="list-group-item list-group-item-action flex-column align-items-start my-1 meal">
<div class="d-flex w-100 justify-content-between align-items-center">
<h5 class="mb-1"></h5>
</div>
<div class="d-flex">
<table class="mb-1 text-start">
<tr><td>Date:</td><td></td></tr>
<tr><td>Time:</td><td></td></tr>
<tr><td>Owner:</td><td></td></tr>
</table>
</div>
</a>
</template>
<script src="scripts/defaults.js"></script>
<script src="scripts/scripts.js"></script>
<script src="scripts/meals.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
......@@ -41,7 +41,7 @@
</div> <!-- form-group// -->
<div class="input-group mb-2">
<span class="input-group-text"> <i class="fas fa-mobile"></i> </span>
<input class="form-control" name="phone" placeholder="Phone number" type="text"
<input class="form-control" name="phone_number" placeholder="Phone number" type="text"
required>
</div> <!-- form-group// -->
<div class="input-group mb-2">
......@@ -56,7 +56,7 @@
</div> <!-- form-group// -->
<div class="input-group mb-2">
<span class="input-group-text"> <i class="fas fa-hotel"></i> </span>
<input class="form-control" name="address" placeholder="Street address" type="text"
<input class="form-control" name="street_address" placeholder="Street address" type="text"
required>
</div> <!-- form-group// -->
<div class="d-grid">
......
......@@ -4,9 +4,36 @@ let deleteButton;
let editButton;
let oldFormData;
class MuscleGroup {
constructor(type) {
this.isValidType = false;
this.validTypes = ["Legs", "Chest", "Back", "Arms", "Abdomen", "Shoulders"]
this.type = this.validTypes.includes(type) ? type : undefined;
};
setMuscleGroupType = (newType) => {
this.isValidType = false;
if(this.validTypes.includes(newType)){
this.isValidType = true;
this.type = newType;
}
else{
alert("Invalid muscle group!");
}
};
getMuscleGroupType = () => {
console.log(this.type, "SWIOEFIWEUFH")
return this.type;
}
}
function handleCancelButtonDuringEdit() {
setReadOnly(true, "#form-exercise");
document.querySelector("select").setAttribute("disabled", "")
okButton.className += " hide";
deleteButton.className += " hide";
cancelButton.className += " hide";
......@@ -17,10 +44,16 @@ function handleCancelButtonDuringEdit() {
let form = document.querySelector("#form-exercise");
if (oldFormData.has("name")) form.name.value = oldFormData.get("name");
if (oldFormData.has("description")) form.description.value = oldFormData.get("description");
if (oldFormData.has("duration")) form.duration.value = oldFormData.get("duration");
if (oldFormData.has("calories")) form.calories.value = oldFormData.get("calories");
if (oldFormData.has("muscleGroup")) form.muscleGroup.value = oldFormData.get("muscleGroup");
if (oldFormData.has("unit")) form.unit.value = oldFormData.get("unit");
oldFormData.delete("name");
oldFormData.delete("description");
oldFormData.delete("duration");
oldFormData.delete("calories");
oldFormData.delete("muscleGroup");
oldFormData.delete("unit");
}
......@@ -30,10 +63,14 @@ function handleCancelButtonDuringCreate() {
}
async function createExercise() {
document.querySelector("select").removeAttribute("disabled")
let form = document.querySelector("#form-exercise");
let formData = new FormData(form);
let body = {"name": formData.get("name"),
"description": formData.get("description"),
"description": formData.get("description"),
"duration": formData.get("duration"),
"calories": formData.get("calories"),
"muscleGroup": formData.get("muscleGroup"),
"unit": formData.get("unit")};
let response = await sendRequest("POST", `${HOST}/api/exercises/`, body);
......@@ -50,6 +87,8 @@ async function createExercise() {
function handleEditExerciseButtonClick() {
setReadOnly(false, "#form-exercise");
document.querySelector("select").removeAttribute("disabled")
editButton.className += " hide";
okButton.className = okButton.className.replace(" hide", "");
cancelButton.className = cancelButton.className.replace(" hide", "");
......@@ -74,30 +113,44 @@ async function deleteExercise(id) {
async function retrieveExercise(id) {
let response = await sendRequest("GET", `${HOST}/api/exercises/${id}/`);
console.log(response.ok);
console.log(response.ok)
if (!response.ok) {
let data = await response.json();
let alert = createAlert("Could not retrieve exercise data!", data);
document.body.prepend(alert);
} else {
document.querySelector("select").removeAttribute("disabled")
let exerciseData = await response.json();
let form = document.querySelector("#form-exercise");
let formData = new FormData(form);
for (let key of formData.keys()) {
let selector = `input[name="${key}"], textarea[name="${key}"]`;
let selector
key !== "muscleGroup" ? selector = `input[name="${key}"], textarea[name="${key}"]` : selector = `select[name=${key}]`
let input = form.querySelector(selector);
let newVal = exerciseData[key];
input.value = newVal;
}
document.querySelector("select").setAttribute("disabled", "")
}
}
async function updateExercise(id) {
let form = document.querySelector("#form-exercise");
let formData = new FormData(form);
let muscleGroupSelector = document.querySelector("select")
muscleGroupSelector.removeAttribute("disabled")
let selectedMuscleGroup = new MuscleGroup(formData.get("muscleGroup"));
let body = {"name": formData.get("name"),
"description": formData.get("description"),
"description": formData.get("description"),
"duration": formData.get("duration"),
"calories": formData.get("calories"),
"muscleGroup": selectedMuscleGroup.getMuscleGroupType(),
"unit": formData.get("unit")};
let response = await sendRequest("PUT", `${HOST}/api/exercises/${id}/`, body);
......@@ -106,6 +159,7 @@ async function updateExercise(id) {
let alert = createAlert(`Could not update exercise ${id}`, data);
document.body.prepend(alert);
} else {
muscleGroupSelector.setAttribute("disabled", "")
// duplicate code from handleCancelButtonDuringEdit
// you should refactor this
setReadOnly(true, "#form-exercise");
......@@ -118,6 +172,9 @@ async function updateExercise(id) {
oldFormData.delete("name");
oldFormData.delete("description");
oldFormData.delete("duration");
oldFormData.delete("calories");
oldFormData.delete("muscleGroup");
oldFormData.delete("unit");
}
}
......
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
let cancelMealButton;
let okMealButton;
let deleteMealButton;
let editMealButton;
async function retrieveMeal(id) {
let mealData = null;
let response = await sendRequest("GET", `${HOST}/api/meals/${id}/`);
if (!response.ok) {
let data = await response.json();
let alert = createAlert("Could not retrieve your meal data!", data);
document.body.prepend(alert);
} else {
mealData = await response.json();
let form = document.querySelector("#form-meal");
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 = mealData[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");
// files
let filesDiv = document.querySelector("#uploaded-files");
for (let file of mealData.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);
}
}
return mealData;
}
function handleCancelDuringMealEdit() {
location.reload();
}
function handleEditMealButtonClick() {
setReadOnly(false, "#form-meal");
document.querySelector("#inputOwner").readOnly = true; // owner field should still be readonly
editMealButton.className += " hide"; // The edit button should be hidden when in edit mode
okMealButton.className = okMealButton.className.replace(" hide", ""); // The ok button should not be hidden when in edit mode
cancelMealButton.className = cancelMealButton.className.replace(" hide", ""); // See above
deleteMealButton.className = deleteMealButton.className.replace(" hide", ""); // See above
cancelMealButton.addEventListener("click", handleCancelDuringMealEdit);
}
async function deleteMeal(id) {
let response = await sendRequest("DELETE", `${HOST}/api/meals/${id}/`);
if (!response.ok) {
let data = await response.json();
let alert = createAlert(`Could not delete this meal. ID: ${id}!`, data);
document.body.prepend(alert);
} else {
window.location.replace("meals.html");
}
}
async function updateMeal(id) {
let submitForm = generateMealForm();
let response = await sendRequest("PUT", `${HOST}/api/meals/${id}/`, submitForm, "");
if (!response.ok) {
let data = await response.json();
let alert = createAlert("Could not update your meal! :-( ", data);
document.body.prepend(alert);
} else {
location.reload();
}
}
function generateMealForm() {
let form = document.querySelector("#form-meal");
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("calories", formData.get("calories"));
// Adds the files
for (let file of formData.getAll("files")) {
submitForm.append("files", file);
}
return submitForm;
}
async function createMeal() {
let submitForm = generateMealForm();
let response = await sendRequest("POST", `${HOST}/api/meals/`, submitForm, "");
if (response.ok) {
window.location.replace("meals.html");
} else {
let data = await response.json();
let alert = createAlert("Could not create new meal", data);
document.body.prepend(alert);
}
}
function handleCancelDuringMealCreate() {
window.location.replace("meals.html");
}
window.addEventListener("DOMContentLoaded", async () => {
cancelMealButton = document.querySelector("#btn-cancel-meal");
okMealButton = document.querySelector("#btn-ok-meal");
deleteMealButton = document.querySelector("#btn-delete-meal");
editMealButton = document.querySelector("#btn-edit-meal");
const urlParams = new URLSearchParams(window.location.search);
let currentUser = await getCurrentUser();
if (urlParams.has('id')) {
const id = urlParams.get('id');
let mealData = await retrieveMeal(id);
if (mealData["owner"] == currentUser.url) {
editMealButton.classList.remove("hide");
editMealButton.addEventListener("click", handleEditMealButtonClick);
deleteMealButton.addEventListener("click", (async (id) => await deleteMeal(id)).bind(undefined, id));
okMealButton.addEventListener("click", (async (id) => await updateMeal(id)).bind(undefined, id));
}
} else {
let ownerInput = document.querySelector("#inputOwner");
ownerInput.value = currentUser.username;
setReadOnly(false, "#form-meal");
ownerInput.readOnly = !ownerInput.readOnly;
okMealButton.className = okMealButton.className.replace(" hide", "");
cancelMealButton.className = cancelMealButton.className.replace(" hide", "");
okMealButton.addEventListener("click", async () => await createMeal());
cancelMealButton.addEventListener("click", handleCancelDuringMealCreate);
}
});
\ No newline at end of file
async function fetchMeals(ordering) {
let response = await sendRequest("GET", `${HOST}/api/meals/?ordering=${ordering}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
} else {
let data = await response.json();
let meals = data.results;
let container = document.getElementById('div-content');
meals.forEach(meal => {
let templateMeal = document.querySelector("#template-meal");
let cloneMeal = templateMeal.content.cloneNode(true);
let aMeal = cloneMeal.querySelector("a");
aMeal.href = `meal.html?id=${meal.id}`;
let h5 = aMeal.querySelector("h5");
h5.textContent = meal.name;
let localDate = new Date(meal.date);
let table = aMeal.querySelector("table");
let rows = table.querySelectorAll("tr");
rows[0].querySelectorAll("td")[1].textContent = localDate.toLocaleDateString(); // Date
rows[1].querySelectorAll("td")[1].textContent = localDate.toLocaleTimeString(); // Time
rows[2].querySelectorAll("td")[1].textContent = meal.owner_username; //Owner
container.appendChild(aMeal);
});
return meals;
}
}
function createMeal() {
window.location.replace("meal.html");
}
window.addEventListener("DOMContentLoaded", async () => {
let createButton = document.querySelector("#btn-create-meal");
createButton.addEventListener("click", createMeal);
let ordering = "-date";
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.has('ordering')) {
let aSort = null;
ordering = urlParams.get('ordering');
if (ordering == "name" || ordering == "owner" || ordering == "date") {
let aSort = document.querySelector(`a[href="?ordering=${ordering}"`);
aSort.href = `?ordering=-${ordering}`;
}
}
let currentSort = document.querySelector("#current-sort");
currentSort.innerHTML = (ordering.startsWith("-") ? "Descending" : "Ascending") + " " + ordering.replace("-", "");
let currentUser = await getCurrentUser();
// grab username
if (ordering.includes("owner")) {
ordering += "__username";
}
let meals = await fetchMeals(ordering);
let tabEls = document.querySelectorAll('a[data-bs-toggle="list"]');
for (let i = 0; i < tabEls.length; i++) {
let tabEl = tabEls[i];
tabEl.addEventListener('show.bs.tab', function (event) {
let mealAnchors = document.querySelectorAll('.meal');
for (let j = 0; j < meals.length; j++) {
// I'm assuming that the order of meal objects matches
// the other of the meal anchor elements. They should, given
// that I just created them.
let meal = meals[j];
let mealAnchor = mealAnchors[j];
switch (event.currentTarget.id) {
case "list-my-meals-list":
if (meal.owner == currentUser.url) {
mealAnchor.classList.remove('hide');
} else {
mealAnchor.classList.add('hide');
}
break;
default :
mealAnchor.classList.remove('hide');
break;
}
}
});
}
});
\ No newline at end of file
......@@ -18,6 +18,7 @@ class NavBar extends HTMLElement {
<a class="nav-link hide" id="nav-exercises" href="exercises.html">Exercises</a>
<a class="nav-link hide" id="nav-mycoach" href="mycoach.html">Coach</a>
<a class="nav-link hide" id="nav-myathletes" href="myathletes.html">Athletes</a>
<a class="nav-link hide" id="nav-meals" href="meals.html">Meal registration</a>
<hr>
</div>
<div class="my-2 my-lg-0 me-5">
......
......@@ -11,12 +11,12 @@ async function createNewUser(event) {
} else {
let body = {
username: formData.get("username"),
password: formData.get("password"),
phone: formData.get("phone"),
country: formData.get("country"),
city: formData.get("city"),
address: formData.get("address")
username: formData.get("username"),
password: formData.get("password"),
phone_number: formData.get("phone_number"),
country: formData.get("country"),
city: formData.get("city"),
street_address: formData.get("street_address")
};
response = await sendRequest("POST", `${HOST}/api/token/`, body);
if (response.ok) {
......
......@@ -22,6 +22,8 @@ function updateNavBar() {
makeNavLinkActive("nav-mycoach")
} else if (window.location.pathname == "/myathletes.html") {
makeNavLinkActive("nav-myathletes");
} else if (window.location.pathname == "/meals.html") {
makeNavLinkActive("nav-myathletes");
}
if (isUserAuthenticated()) {
......@@ -32,6 +34,7 @@ function updateNavBar() {
document.querySelector('a[href="mycoach.html"').classList.remove("hide");
document.querySelector('a[href="exercises.html"').classList.remove("hide");
document.querySelector('a[href="myathletes.html"').classList.remove("hide");
document.querySelector('a[href="meals.html"').classList.remove("hide");
} else {
document.getElementById("btn-login-nav").classList.remove("hide");
document.getElementById("btn-register").classList.remove("hide");
......
......@@ -3,6 +3,7 @@ let okWorkoutButton;
let deleteWorkoutButton;
let editWorkoutButton;
let postCommentButton;
let galleryButton;
async function retrieveWorkout(id) {
let workoutData = null;
......@@ -41,7 +42,9 @@ async function retrieveWorkout(id) {
let pathArray = file.file.split("/");
a.text = pathArray[pathArray.length - 1];
a.className = "me-2";
filesDiv.appendChild(a);
}
// create exercises
......@@ -114,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", "");
......@@ -124,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) {
......@@ -309,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");
......@@ -317,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();
......@@ -344,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);
......
......@@ -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
......@@ -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>
......