Skip to content
Snippets Groups Projects
Commit 3e9ef7cf authored by Simen Frium Opedal's avatar Simen Frium Opedal
Browse files

Merge branch 'feat/34-fitnessProfile' into 'master'

Merge 34-fitnessProfile into master

See merge request !2
parents ebc2ecb7 268325b2
No related branches found
No related tags found
1 merge request!2Merge 34-fitnessProfile into master
Pipeline #161973 passed
/env
\ No newline at end of file
......@@ -18,6 +18,10 @@ class User(AbstractUser):
country = models.TextField(max_length=50, blank=True)
city = models.TextField(max_length=50, blank=True)
street_address = models.TextField(max_length=50, blank=True)
age = models.PositiveIntegerField(blank=True, null=True)
expirience = models.PositiveIntegerField(blank=True, null=True)
favorite_dicipline = models.TextField(max_length=50, blank=True, null=True)
bio = models.TextField(max_length=200, blank=True, null=True)
def athlete_directory_path(instance, filename):
......
......@@ -73,21 +73,47 @@ class UserGetSerializer(serializers.HyperlinkedModelSerializer):
"workouts",
"coach_files",
"athlete_files",
"age",
"expirience",
"favorite_dicipline",
"bio"
]
class UserPutSerializer(serializers.ModelSerializer):
class Meta:
model = get_user_model()
fields = ["athletes"]
fields = [
"athletes",
"age",
"expirience",
"favorite_dicipline",
"bio"
]
def update(self, instance, validated_data):
athletes_data = validated_data["athletes"]
age_data = validated_data["age"]
expirience_data = validated_data["expirience"]
favorite_dicipline_data = validated_data["favorite_dicipline"]
bio_data = validated_data["bio"]
instance.athletes.set(athletes_data)
instance.age.set(age_data)
instance.expirience.set(expirience_data)
instance.favorite_dicipline.set(favorite_dicipline_data)
instance.bio.set(bio_data)
return instance
class ProfilePutSerializer(serializers.ModelSerializer):
class Meta:
model = get_user_model()
fields = [
"age",
"expirience",
"favorite_dicipline",
"bio"
]
class AthleteFileSerializer(serializers.HyperlinkedModelSerializer):
owner = serializers.ReadOnlyField(source="owner.username")
......
......@@ -5,6 +5,7 @@ from rest_framework.urlpatterns import format_suffix_patterns
urlpatterns = [
path("api/users/", views.UserList.as_view(), name="user-list"),
path("api/users/<int:pk>/", views.UserDetail.as_view(), name="user-detail"),
path("api/profiles/<int:pk>/", views.ProfileUpdate.as_view(), name="profile-update"),
path("api/users/<str:username>/", views.UserDetail.as_view(), name="user-detail"),
path("api/offers/", views.OfferList.as_view(), name="offer-list"),
path("api/offers/<int:pk>/", views.OfferDetail.as_view(), name="offer-detail"),
......
......@@ -8,6 +8,7 @@ from users.serializers import (
AthleteFileSerializer,
UserPutSerializer,
UserGetSerializer,
ProfilePutSerializer
)
from rest_framework.permissions import (
AllowAny,
......@@ -81,6 +82,17 @@ class UserDetail(
def patch(self, request, *args, **kwargs):
return self.partial_update(request, *args, **kwargs)
class ProfileUpdate(
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
generics.GenericAPIView,
):
serializer_class = ProfilePutSerializer
queryset = get_user_model().objects.all()
permission_classes = [permissions.IsAuthenticated & IsCurrentUser]
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
class OfferList(
mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView
......@@ -195,4 +207,4 @@ class AthleteFileDetail(
return self.retrieve(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
return self.destroy(request, *args, **kwargs)
\ 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>Group</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" id="title"></h3>
<p>Edit you fitness information by clicking the edit button below!</p>
</div>
</div>
<form class="row g-3" id="form-profile">
<div class="col-lg-6 ">
<label for="inputAge" class="form-label">Age</label>
<input type="number" class="form-control" id="inputAge" name="age" readonly>
</div>
<div class="col-lg-6"></div>
<div class="col-lg-6">
<label for="inputExpirience" class="form-label">Workout experience(years)</label>
<input type="number" class="form-control" id="inputExpirience" name="expirience" readonly>
</div>
<div class="col-lg-6"></div>
<div class="col-lg-6">
<label for="inputDicipline" class="form-label">Favorite diciplines</label>
<textarea class="form-control" id="inputDicipline" name="favorite_dicipline" readonly></textarea>
</div>
<div class="col-lg-6"></div>
<div class="col-lg-6">
<label for="inputBio" class="form-label">About me</label>
<textarea class="form-control" id="inputBio" name="bio" readonly></textarea>
</div>
<div class="col-lg-6"></div>
<div class="col-lg-6">
<input type="button" class="btn btn-primary hide" id="btn-ok-profile" value=" OK ">
<input type="button" class="btn btn-secondary hide" id="btn-cancel-profile" value="Cancel">
<input type="button" class="btn btn-primary" id="btn-edit-profile" value=" Edit ">
</div>
<div class="col-lg-6"></div>
</form>
</div>
<script src="scripts/defaults.js"></script>
<script src="scripts/scripts.js"></script>
<script src="scripts/profile.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
......@@ -20,6 +20,7 @@ class NavBar extends HTMLElement {
<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>
<a class="nav-link hide" id="nav-groups" href="groups.html">Groups</a>
<a class="nav-link hide" id="nav-profile" href="profile.html">Fitness Profile</a>
<hr>
</div>
<div class="my-2 my-lg-0 me-5">
......
let cancelButton;
let okButton;
let editButton;
let oldFormData;
function handleCancelButtonDuringCreate() {
window.location.replace("profile.html");
}
/**
* If user presses "cancel" during editing a group the
* form fields become read only and the form data is deleted.
* And the buttons change.
*/
function handleCancelButtonDuringEdit() {
setReadOnly(true, "#form-profile");
okButton.className += " hide";
cancelButton.className += " hide";
editButton.className = editButton.className.replace(" hide", "");
cancelButton.removeEventListener("click", handleCancelButtonDuringEdit);
let form = document.querySelector("#form-profile");
if (oldFormData.has("age")) form.name.value = oldFormData.get("age");
if (oldFormData.has("expirience")) form.description.value = oldFormData.get("expirience");
if (oldFormData.has("dicipline")) form.description.value = oldFormData.get("dicipline");
if (oldFormData.has("bio")) form.description.value = oldFormData.get("bio");
oldFormData.delete("age");
oldFormData.delete("expirience");
oldFormData.delete("dicipline");
oldFormData.delete("bio");
}
/**
* If the user clicks on the edit button the form fields can be edited.
* And the form is updated with the data of the group that the user wants to edit.
*/
function handleEditProfileButtonClick() {
setReadOnly(false, "#form-profile");
editButton.className += " hide";
okButton.className = okButton.className.replace(" hide", "");
cancelButton.className = cancelButton.className.replace(" hide", "");
cancelButton.addEventListener("click", handleCancelButtonDuringEdit);
let form = document.querySelector("#form-profile");
oldFormData = new FormData(form);
}
/**
* sends an API request to retrieve the profile information of the user that is currently active
*/
async function getCurrentProfile() {
let res = await sendRequest("GET", `${HOST}/api/users/?user=current`);
if (!res.ok) {
console.log("COULD NOT RETRIEVE CURRENTLY LOGGED IN USER");
} else {
let data = await res.json();
userID = data.results[0].id;
userName = data.results[0].username
}
document.getElementById("title").innerHTML = userName + "'s Fitness Profile";
let response = await sendRequest("GET", `${HOST}/api/users/${userID}/`);
if (!response.ok) {
console.log("COULD NOT RETRIEVE CURRENTLY LOGGED IN USER");
} else {
let profileData = await response.json();
let form = document.querySelector("#form-profile");
let formData = new FormData(form);
for (let key of formData.keys()) {
let selector
selector = `input[name="${key}"], textarea[name="${key}"]`;
let input = form.querySelector(selector);
let newVal = profileData[key];
input.value = newVal;
}
}
}
/**
* Sends a PUT request to the API to update a group's information.
* @param {integer} id of the group to be updated
*/
async function updateProfile() {
let res = await sendRequest("GET", `${HOST}/api/users/?user=current`);
if (!res.ok) {
console.log("COULD NOT RETRIEVE CURRENTLY LOGGED IN USER");
} else {
let data = await res.json();
userID = data.results[0].id;
}
let form = document.querySelector("#form-profile");
let formData = new FormData(form);
let body = {"age": formData.get("age"),
"expirience": formData.get("expirience"),
"favorite_dicipline": formData.get("favorite_dicipline"),
"bio": formData.get("bio"),
};
let response = await sendRequest("PUT", `${HOST}/api/profiles/${userID}/`, body);
if (!response.ok) {
let data = await response.json();
let alert = createAlert(`Could not update profile`, data);
document.body.prepend(alert);
} else {
setReadOnly(true, "#form-profile");
okButton.className += " hide";
cancelButton.className += " hide";
editButton.className = editButton.className.replace(" hide", "");
cancelButton.removeEventListener("click", handleCancelButtonDuringEdit);
oldFormData.delete("age");
oldFormData.delete("expirience");
oldFormData.delete("dicipline");
oldFormData.delete("bio");
}
}
/**
* When a user enters the group.html this decides whether it
* is entered in view/edit mode or in create mode. If the html contains
* a url parameter with an id it is entered in view/edit mode.
*/
window.addEventListener("DOMContentLoaded", async () => {
cancelButton = document.querySelector("#btn-cancel-profile");
okButton = document.querySelector("#btn-ok-profile");
editButton = document.querySelector("#btn-edit-profile");
oldFormData = null;
await getCurrentProfile();
editButton.addEventListener("click", handleEditProfileButtonClick);
okButton.addEventListener("click", async () => await updateProfile());
});
\ No newline at end of file
......@@ -27,6 +27,9 @@ function updateNavBar() {
} else if (window.location.pathname == "/groups.html") {
makeNavLinkActive("nav-groups");
}
else if (window.location.pathname == "/profile.html") {
makeNavLinkActive("nav-profile");
}
if (isUserAuthenticated()) {
document.getElementById("btn-logout").classList.remove("hide");
......@@ -38,6 +41,7 @@ function updateNavBar() {
document.querySelector('a[href="myathletes.html"').classList.remove("hide");
document.querySelector('a[href="meals.html"').classList.remove("hide");
document.querySelector('a[href="groups.html"').classList.remove("hide");
document.querySelector('a[href="profile.html"').classList.remove("hide");
} else {
document.getElementById("btn-login-nav").classList.remove("hide");
document.getElementById("btn-register").classList.remove("hide");
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment