Skip to content
Snippets Groups Projects
Commit 53695731 authored by Haakon Gunleiksrud's avatar Haakon Gunleiksrud
Browse files

Merge branch 'feat/meal_module' into 'master'

Feat/meal module

See merge request aasmuha/tdt4242-base!17
parents 0719e982 928238a1
No related branches found
No related tags found
No related merge requests found
Showing
with 910 additions and 3 deletions
"""Module for registering models from meals app to admin page so that they appear
"""
from django.contrib import admin
# Register your models here.
from .models import Meal, MealFile
admin.site.register(Meal)
admin.site.register(MealFile)
"""AppConfig for meals app
"""
from django.apps import AppConfig
class MealsConfig(AppConfig):
"""AppConfig for meals app
Attributes:
name (str): The name of the application
"""
name = "meals"
# Generated by Django 3.1 on 2021-10-20 15:17
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import meals.models
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Meal',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100)),
('date', models.DateTimeField()),
('notes', models.TextField()),
('calories', models.IntegerField()),
('is_veg', models.BooleanField(default=False)),
('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='meals', to=settings.AUTH_USER_MODEL)),
],
options={
'ordering': ['-date'],
},
),
migrations.CreateModel(
name='MealFile',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('file', models.FileField(upload_to=meals.models.meal_directory_path)),
('meal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='files', to='meals.meal')),
('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='meal_files', to=settings.AUTH_USER_MODEL)),
],
),
]
"""
Mixins for the meals application
"""
class CreateListModelMixin(object):
"""Mixin that allows to create multiple objects from lists.
Taken from https://stackoverflow.com/a/48885641
"""
def get_serializer(self, *args, **kwargs):
"""If an array is passed, set serializer to many.
kwargs["many"] will be set to true if an array is passed. This argument
is passed when retrieving the serializer.
Args:
*args: Variable length argument list passed to the serializer.
**kwargs: Arbitrary keyword arguments passed to the serializer, including "many".
Returns:
[type]: [description]
"""
if isinstance(kwargs.get("data", {}), list):
kwargs["many"] = True
return super(CreateListModelMixin, self).get_serializer(*args, **kwargs)
"""Contains the models for the meals Django application. Users
log meals (Meal). The user can also upload files (MealFile) .
"""
import os
from django.db import models
from django.core.files.storage import FileSystemStorage
from django.conf import settings
from django.contrib.auth import get_user_model
class OverwriteStorage(FileSystemStorage):
"""Filesystem storage for overwriting files. Currently unused."""
def get_available_name(self, name, max_length=None):
"""https://djangosnippets.org/snippets/976/
Returns a filename that's free on the target storage system, and
available for new content to be written to.
Args:
name (str): Name of the file
max_length (int, optional): Maximum length of a file name. Defaults to None.
"""
if self.exists(name):
os.remove(os.path.join(settings.MEDIA_ROOT, name))
# Create your models here.
class Meal(models.Model):
"""Django model for a meal that users can log.
A meal has several attributes, and files uploaded by the user.
Attributes:
name: Name of the meal
date: Date and time the meal was consumed
notes: Notes about the meal
calories: Total amount of calories in the meal
is_veg: Whether the meal was vegetarian or not
owner: User that logged the meal
"""
name = models.CharField(max_length=100)
date = models.DateTimeField()
notes = models.TextField()
calories = models.IntegerField()
is_veg = models.BooleanField(default=False)
owner = models.ForeignKey(
get_user_model(), on_delete=models.CASCADE, related_name="meals"
)
class Meta:
ordering = ["-date"]
def __str__(self):
return self.name
def meal_directory_path(instance, filename):
"""Return path for which meal files should be uploaded on the web server
Args:
instance (MealFile): MealFile instance
filename (str): Name of the file
Returns:
str: Path where workout file is stored
"""
return f"meals/{instance.meal.id}/{filename}"
class MealFile(models.Model):
"""Django model for file associated with a meal. Basically a wrapper.
Attributes:
meal: The meal for which this file has been uploaded
owner: The user who uploaded the file
file: The actual file that's being uploaded
"""
meal = models.ForeignKey(Meal, on_delete=models.CASCADE, related_name="files")
owner = models.ForeignKey(
get_user_model(), on_delete=models.CASCADE, related_name="meal_files"
)
file = models.FileField(upload_to=meal_directory_path)
"""Contains custom parsers for serializers from the meals Django app
"""
import json
from rest_framework import parsers
# Thanks to https://stackoverflow.com/a/50514630
class MultipartJsonParser(parsers.MultiPartParser):
"""Parser for serializing multipart data containing both files and JSON.
This is currently unused.
"""
def parse(self, stream, media_type=None, parser_context=None):
result = super().parse(
stream, media_type=media_type, parser_context=parser_context
)
data = {}
new_files = {"files": []}
# for case1 with nested serializers
# parse each field with json
for key, value in result.data.items():
if not isinstance(value, str):
data[key] = value
continue
if "{" in value or "[" in value:
try:
data[key] = json.loads(value)
except ValueError:
data[key] = value
else:
data[key] = value
files = result.files.getlist("files")
for file in files:
new_files["files"].append({"file": file})
return parsers.DataAndFiles(data, new_files)
"""Contains custom DRF permissions classes for the meals app
"""
from rest_framework import permissions
from meals.models import Meal
class IsOwner(permissions.BasePermission):
"""Checks whether the requesting user is also the owner of the existing object"""
def has_object_permission(self, request, view, obj):
return obj.owner == request.user
class IsOwnerOfMeal(permissions.BasePermission):
"""Checks whether the requesting user is also the owner of the new or existing object"""
def has_permission(self, request, view):
if request.method == "POST":
if request.data.get("meal"):
meal_id = request.data["meal"].split("/")[-2]
meal = Meal.objects.get(pk=meal_id)
if meal:
return meal.owner == request.user
return False
return True
def has_object_permission(self, request, view, obj):
return obj.meal.owner == request.user
class IsReadOnly(permissions.BasePermission):
"""Checks whether the HTTP request verb is only for retrieving data (GET, HEAD, OPTIONS)"""
def has_object_permission(self, request, view, obj):
return request.method in permissions.SAFE_METHODS
"""Serializers for the meals application
"""
from rest_framework import serializers
from rest_framework.serializers import HyperlinkedRelatedField
from meals.models import Meal, MealFile
class MealFileSerializer(serializers.HyperlinkedModelSerializer):
"""Serializer for a MealFile. Hyperlinks are used for relationships by default.
Serialized fields: url, id, owner, file, meal
Attributes:
owner: The owner (User) of the MealFile, represented by a username. ReadOnly
meal: The associate meal for this MealFile, represented by a hyperlink
"""
owner = serializers.ReadOnlyField(source="owner.username")
meal = HyperlinkedRelatedField(
queryset=Meal.objects.all(), view_name="meal-detail", required=False
)
class Meta:
model = MealFile
fields = ["url", "id", "owner", "file", "meal"]
def create(self, validated_data):
return MealFile.objects.create(**validated_data)
class MealSerializer(serializers.HyperlinkedModelSerializer):
"""Serializer for a Meal. Hyperlinks are used for relationships by default.
This serializer specifies nested serialization since a meal consists of MealFiles.
Serialized fields: url, id, name, date, notes, calories, owner, is_veg, owner_username, files
Attributes:
owner_username: Username of the owning User
files: Serializer for MealFiles
"""
owner_username = serializers.SerializerMethodField()
files = MealFileSerializer(many=True, required=False)
class Meta:
model = Meal
fields = [
"url",
"id",
"name",
"date",
"notes",
"calories",
"is_veg",
"owner",
"owner_username",
"files",
]
extra_kwargs = {"owner": {"read_only": True}}
def create(self, validated_data):
"""Custom logic for creating MealFiles, and a Meal.
This is needed to iterate over the files, since this serializer is nested.
Args:
validated_data: Validated files
Returns:
Meal: A newly created Meal
"""
files_data = []
if "files" in validated_data:
files_data = validated_data.pop("files")
meal = Meal.objects.create(**validated_data)
for file_data in files_data:
MealFile.objects.create(
meal=meal, owner=meal.owner, file=file_data.get("file")
)
return meal
def update(self, instance, validated_data):
"""Custom logic for updating a Meal.
This is needed because each object in files must be iterated
over and handled individually.
Args:
instance (Meal): Current Meal object
validated_data: Contains data for validated fields
Returns:
Meal: Updated Meal instance
"""
instance.name = validated_data.get("name", instance.name)
instance.date = validated_data.get("date", instance.date)
instance.notes = validated_data.get("notes", instance.notes)
instance.is_veg = validated_data.get("is_veg", instance.is_veg)
instance.calories = validated_data.get("calories", instance.calories)
instance.save()
# Handle MealFiles
if "files" in validated_data:
files_data = validated_data.pop("files")
files = instance.files
for file, file_data in zip(files.all(), files_data):
file.file = file_data.get("file", file.file)
# If new files have been added, creating new MealFiles
if len(files_data) > len(files.all()):
for i in range(len(files.all()), len(files_data)):
MealFile.objects.create(
meal=instance,
owner=instance.owner,
file=files_data[i].get("file"),
)
# Else if files have been removed, delete MealFiles
elif len(files_data) < len(files.all()):
for i in range(len(files_data), len(files.all())):
files.all()[i].delete()
return instance
def get_owner_username(self, obj):
"""Returns the owning user's username
Args:
obj (Meal): Current Meal
Returns:
str: Username of owner
"""
return obj.owner.username
\ No newline at end of file
"""
Tests for the meals application.
"""
from django.test import TestCase
# Create your tests here.
from django.urls import path, include
from meals import views
from rest_framework.urlpatterns import format_suffix_patterns
from rest_framework_simplejwt.views import (
TokenObtainPairView,
TokenRefreshView,
)
# This is messy and should be refactored
urlpatterns = format_suffix_patterns(
[
path("", views.api_root),
path("api/meals/", views.MealList.as_view(), name="meal-list"),
path(
"api/meals/<int:pk>/",
views.MealDetail.as_view(),
name="meal-detail",
),
path(
"api/meal-files/",
views.MealFileList.as_view(),
name="meal-file-list",
),
path(
"api/meal-files/<int:pk>/",
views.MealFileDetail.as_view(),
name="mealfile-detail",
),
path("", include("users.urls")),
]
)
"""Contains views for the meals application. These are mostly class-based views.
"""
from rest_framework import generics, mixins
from rest_framework import permissions
from rest_framework.parsers import (
JSONParser,
)
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.reverse import reverse
from django.db.models import Q
from rest_framework import filters
from meals.parsers import MultipartJsonParser
from meals.permissions import (
IsOwner,
IsOwnerOfMeal,
IsReadOnly,
)
from meals.mixins import CreateListModelMixin
from meals.models import Meal, MealFile
from meals.serializers import MealSerializer
from meals.serializers import MealFileSerializer
from django.core.exceptions import PermissionDenied
from rest_framework_simplejwt.tokens import RefreshToken
from rest_framework.response import Response
import json
from collections import namedtuple
import base64, pickle
from django.core.signing import Signer
@api_view(["GET"])
def api_root(request, format=None):
return Response(
{
"users": reverse("user-list", request=request, format=format),
"meals": reverse("meal-list", request=request, format=format),
"meal-files": reverse(
"meal-file-list", request=request, format=format
),
}
)
class MealList(
mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView
):
"""Class defining the web response for the creation of a Meal, or displaying a list
of Meals
HTTP methods: GET, POST
"""
serializer_class = MealSerializer
permission_classes = [
permissions.IsAuthenticated
] # User must be authenticated to create/view meals
parser_classes = [
MultipartJsonParser,
JSONParser,
] # For parsing JSON and Multi-part requests
filter_backends = [filters.OrderingFilter]
ordering_fields = ["name", "date", "owner__username"]
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
def get_queryset(self):
qs = Meal.objects.none()
if self.request.user:
qs = Meal.objects.filter(Q(owner=self.request.user)).distinct()
return qs
class MealDetail(
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
generics.GenericAPIView,
):
"""Class defining the web response for the details of an individual Meal.
HTTP methods: GET, PUT, DELETE
"""
queryset = Meal.objects.all()
serializer_class = MealSerializer
permission_classes = [
permissions.IsAuthenticated
& IsOwner
]
parser_classes = [MultipartJsonParser, JSONParser]
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
class MealFileList(
mixins.ListModelMixin,
mixins.CreateModelMixin,
CreateListModelMixin,
generics.GenericAPIView,
):
queryset = MealFile.objects.all()
serializer_class = MealFileSerializer
permission_classes = [permissions.IsAuthenticated & IsOwnerOfMeal]
parser_classes = [MultipartJsonParser, JSONParser]
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
def get_queryset(self):
qs = MealFile.objects.none()
if self.request.user:
qs = MealFile.objects.filter(
Q(owner=self.request.user)).distinct()
return qs
class MealFileDetail(
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
generics.GenericAPIView,
):
queryset = MealFile.objects.all()
serializer_class = MealFileSerializer
permission_classes = [
permissions.IsAuthenticated
& IsOwner
]
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
......@@ -59,6 +59,7 @@ INSTALLED_APPS = [
"django.contrib.staticfiles",
"rest_framework",
"workouts.apps.WorkoutsConfig",
"meals.apps.MealsConfig",
"users.apps.UsersConfig",
"comments.apps.CommentsConfig",
"corsheaders",
......
......@@ -21,6 +21,7 @@ from django.conf.urls.static import static
urlpatterns = [
path("admin/", admin.site.urls),
path("", include("workouts.urls")),
path("", include("meals.urls")),
]
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
......
# 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'),
),
]
<!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
......@@ -6,14 +6,29 @@ let oldFormData;
class MuscleGroup {
constructor(type) {
this.type = type;
this.isValidType = false;
this.validTypes = ["Legs", "Chest", "Back", "Arms", "Abdomen", "Shoulders"]
this.type = this.validTypes.includes(type) ? type : undefined;
};
setMuscleGroupType = (newType) => {
this.type = newType
this.isValidType = false;
if(this.validTypes.includes(newType)){
this.isValidType = true;
this.type = newType;
}
else{
alert("Invalid muscle group!");
}
};
getMuscleGroupType = () => this.type;
getMuscleGroupType = () => {
console.log(this.type, "SWIOEFIWEUFH")
return this.type;
}
}
function handleCancelButtonDuringEdit() {
......
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
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