diff --git a/backend/secfit/groups/admin.py b/backend/secfit/groups/admin.py index bab8ce01038620fa2be80370e192f0288b212d28..4a5b0e737fcd0959191df2fc95335440d88e8e27 100644 --- a/backend/secfit/groups/admin.py +++ b/backend/secfit/groups/admin.py @@ -1,7 +1,10 @@ from django.contrib import admin # Register your models here. -from .models import Group, Membership +from .models import Group, Membership, Content, Comment, Like admin.site.register(Group) admin.site.register(Membership) +admin.site.register(Content) +admin.site.register(Comment) +admin.site.register(Like) diff --git a/backend/secfit/groups/migrations/0002_auto_20220314_1123.py b/backend/secfit/groups/migrations/0002_auto_20220314_1123.py new file mode 100644 index 0000000000000000000000000000000000000000..b03eedd76a1b34ffe23f43bc124d207f4438bbb1 --- /dev/null +++ b/backend/secfit/groups/migrations/0002_auto_20220314_1123.py @@ -0,0 +1,19 @@ +# Generated by Django 3.1 on 2022-03-14 10:23 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('groups', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='membership', + name='group', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='memberOf', to='groups.group'), + ), + ] diff --git a/backend/secfit/groups/migrations/0003_content.py b/backend/secfit/groups/migrations/0003_content.py new file mode 100644 index 0000000000000000000000000000000000000000..76bf596aabf1bf41f1a4bdcc8aabb768ab6d2498 --- /dev/null +++ b/backend/secfit/groups/migrations/0003_content.py @@ -0,0 +1,27 @@ +# Generated by Django 3.1 on 2022-03-14 10:48 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('groups', '0002_auto_20220314_1123'), + ] + + operations = [ + migrations.CreateModel( + name='Content', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.TextField(max_length=100)), + ('description', models.TextField()), + ('image', models.ImageField(blank=True, upload_to='media')), + ('creator', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='creator', to=settings.AUTH_USER_MODEL)), + ('group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ownedByGroup', to='groups.group')), + ], + ), + ] diff --git a/backend/secfit/groups/migrations/0004_comment.py b/backend/secfit/groups/migrations/0004_comment.py new file mode 100644 index 0000000000000000000000000000000000000000..0e9d2ee50f580934501750d410100d321d063182 --- /dev/null +++ b/backend/secfit/groups/migrations/0004_comment.py @@ -0,0 +1,25 @@ +# Generated by Django 3.1 on 2022-03-14 18:17 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('groups', '0003_content'), + ] + + operations = [ + migrations.CreateModel( + name='Comment', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('message', models.TextField()), + ('content', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='relatedContent', to='groups.content')), + ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='commentOwner', to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/backend/secfit/groups/migrations/0005_like.py b/backend/secfit/groups/migrations/0005_like.py new file mode 100644 index 0000000000000000000000000000000000000000..0f9b6869181a63f3c2dd1472b7a4011ba29bbcf5 --- /dev/null +++ b/backend/secfit/groups/migrations/0005_like.py @@ -0,0 +1,27 @@ +# Generated by Django 3.1 on 2022-03-14 20:12 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('groups', '0004_comment'), + ] + + operations = [ + migrations.CreateModel( + name='Like', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('content', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='likedContent', to='groups.content')), + ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='likeOwner', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'unique_together': {('owner', 'content')}, + }, + ), + ] diff --git a/backend/secfit/groups/models.py b/backend/secfit/groups/models.py index f07b6141a38ee0a99e5800fb17c9a22d94ee021d..2488cf8783f4d4e9607a5f4d241e226fd1c8eb77 100644 --- a/backend/secfit/groups/models.py +++ b/backend/secfit/groups/models.py @@ -41,3 +41,53 @@ class Membership(models.Model): class Meta: # restriction so that no user is a member two times in a group. unique_together = ('member', 'group') + + +class Content(models.Model): + creator = models.ForeignKey( + get_user_model(), on_delete=models.CASCADE, related_name="creator" + ) + group = models.ForeignKey( + Group, on_delete=models.CASCADE, related_name="ownedByGroup" + ) + title = models.TextField(max_length=100) + description = models.TextField() + image = models.ImageField(upload_to='media', blank=True) + + +class Comment(models.Model): + """Django model for a comment left on a workout. + + Attributes: + owner: Who posted the comment + workout: The workout this comment was left on. + content: The content of the comment. + timestamp: When the comment was created. + """ + owner = models.ForeignKey( + get_user_model(), on_delete=models.CASCADE, related_name="commentOwner" + ) + content = models.ForeignKey( + Content, on_delete=models.CASCADE, related_name="relatedContent" + ) + message = models.TextField() + + +class Like(models.Model): + """Django model for a comment left on a workout. + + Attributes: + owner: Who posted the comment + workout: The workout this comment was left on. + content: The content of the comment. + timestamp: When the comment was created. + """ + owner = models.ForeignKey( + get_user_model(), on_delete=models.CASCADE, related_name="likeOwner" + ) + content = models.ForeignKey( + Content, on_delete=models.CASCADE, related_name="likedContent" + ) + + class Meta: + unique_together = ('owner', 'content') diff --git a/backend/secfit/groups/serializers.py b/backend/secfit/groups/serializers.py index aa602f2e82a91bc0087988e52fc50d8c19e6bfcc..d4be6d03029eb662c0e5214b15edcfda4e6f3a5e 100644 --- a/backend/secfit/groups/serializers.py +++ b/backend/secfit/groups/serializers.py @@ -1,6 +1,6 @@ from rest_framework import serializers from rest_framework.serializers import HyperlinkedRelatedField -from .models import Group, Membership +from .models import Group, Membership, Content, Comment, Like class GroupSerializer(serializers.HyperlinkedModelSerializer): @@ -26,3 +26,41 @@ class MembershipSerializer(serializers.ModelSerializer): class Meta: model = Membership fields = ["id", "member", "group"] + + +class ContentSerializer(serializers.ModelSerializer): + """Serializer for an Exercise. Hyperlinks are used for relationships by default. + + Serialized fields: url, id, name, description, duration, calories, muscle group, unit, instances + + Attributes: + instances: Associated exercise instances with this Exercise type. Hyperlinks. + """ + + class Meta: + model = Content + fields = ["id", "group", "title", "description", "image"] + creator = serializers.ReadOnlyField(source='creator.id') + image = serializers.Field(required=False) + + +class CommentSerializer(serializers.ModelSerializer): + """Serializer for a membership instance + + Serialized fields: id, member, group + """ + class Meta: + model = Comment + fields = ["id", "content", "message"] + owner = serializers.ReadOnlyField(source="owner.username") + + +class LikeSerializer(serializers.ModelSerializer): + """Serializer for a membership instance + + Serialized fields: id, member, group + """ + class Meta: + model = Like + fields = ["id", "content"] + owner = serializers.ReadOnlyField(source="owner.username") diff --git a/backend/secfit/groups/urls.py b/backend/secfit/groups/urls.py index 32d8282ff3fc2c084290855c61f61facc1bd1eae..a1ba51a81e0c22a218f03c0f315e5f199ece5895 100644 --- a/backend/secfit/groups/urls.py +++ b/backend/secfit/groups/urls.py @@ -1,9 +1,14 @@ from django.urls import path -from .views import GroupView, MembershipView, getGroup +from .views import GroupView, MembershipView, getGroup, ContentList, PostContent, PostComment, CommentList, PostLike urlpatterns = [ path("api/groups/", GroupView.as_view(), name="group-view"), path("api/members/", MembershipView.as_view(), name="membership-view"), path("api/groups/<int:pk>/", getGroup.as_view(), name="updateGroup-view"), + path("api/content/", PostContent.as_view(), name="postContent-view"), + path("api/content/<int:pk>/", ContentList.as_view(), name="contentList-view"), + path("api/comment/", PostComment.as_view(), name="postComment-view"), + path("api/comment/<int:pk>/", CommentList.as_view(), name="CommentList-view"), + path("api/like/", PostLike.as_view(), name="postLike-view"), ] diff --git a/backend/secfit/groups/views.py b/backend/secfit/groups/views.py index aa110a772833a7aa35219d1f30cf7203ac94abcb..5081bfa06e96b5a45fda519184cf4c1308c8acc3 100644 --- a/backend/secfit/groups/views.py +++ b/backend/secfit/groups/views.py @@ -1,8 +1,8 @@ from django.shortcuts import render from rest_framework import generics, mixins -from .models import Group, Membership +from .models import Group, Membership, Content, Comment, Like from rest_framework import permissions -from .serializers import GroupSerializer, MembershipSerializer +from .serializers import GroupSerializer, MembershipSerializer, ContentSerializer, CommentSerializer, LikeSerializer from rest_framework.filters import OrderingFilter @@ -69,3 +69,104 @@ class MembershipView( def post(self, request, *args, **kwargs): return self.create(request, *args, **kwargs) + + +class ContentList( + mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView +): + """Class defining the web response for the creation of content, or + a list of content. + + HTTP methods: GET, POST + """ + + queryset = Content.objects.all() + serializer_class = ContentSerializer + + def get(self, request, *args, **kwargs): + return self.list(request, *args, **kwargs) + + def get_queryset(self): + qs = Content.objects.all() + pk = self.kwargs['pk'] + qs = Content.objects.filter(group=pk) + return qs + + +class PostContent( + mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView +): + """Class defining the web response for the creation of content, or + a list of content. + + HTTP methods: GET, POST + """ + + queryset = Content.objects.all() + serializer_class = ContentSerializer + + def post(self, request, *args, **kwargs): + return self.create(request, *args, **kwargs) + + def perform_create(self, serializer): + serializer.save(creator=self.request.user) + + +class PostComment( + mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView +): + """Class defining the web response for the creation of content, or + a list of content. + + HTTP methods: GET, POST + """ + + queryset = Comment.objects.all() + serializer_class = CommentSerializer + + def post(self, request, *args, **kwargs): + return self.create(request, *args, **kwargs) + + def perform_create(self, serializer): + serializer.save(owner=self.request.user) + + +class CommentList( + mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView +): + """Class defining the web response for the creation of content, or + a list of content. + + HTTP methods: GET, POST + """ + + queryset = Comment.objects.all() + serializer_class = CommentSerializer + + def get(self, request, *args, **kwargs): + return self.list(request, *args, **kwargs) + + def get_queryset(self): + qs = Comment.objects.all() + pk = self.kwargs['pk'] + qs = Comment.objects.filter(content=pk) + return qs + + +class PostLike( + mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView +): + """Class defining the web response for the creation of content, or + a list of content. + + HTTP methods: GET, POST + """ + + queryset = Like.objects.all() + serializer_class = LikeSerializer + + def post(self, request, *args, **kwargs): + return self.create(request, *args, **kwargs) + + def perform_create(self, serializer): + serializer.save(owner=self.request.user) diff --git a/backend/secfit/requirements.txt b/backend/secfit/requirements.txt index 9feb375bde1e8fb7befe6c102dd29beeee7c6940..88fa935213723454c324824ac92816923797c869 100644 Binary files a/backend/secfit/requirements.txt and b/backend/secfit/requirements.txt differ diff --git a/backend/secfit/users/migrations/0010_auto_20220314_1123.py b/backend/secfit/users/migrations/0010_auto_20220314_1123.py new file mode 100644 index 0000000000000000000000000000000000000000..0ee7eccf975fd6d2278d975380c18886be9c755a --- /dev/null +++ b/backend/secfit/users/migrations/0010_auto_20220314_1123.py @@ -0,0 +1,33 @@ +# Generated by Django 3.1 on 2022-03-14 10:23 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0009_auto_20210204_1055'), + ] + + operations = [ + migrations.AddField( + model_name='user', + name='age', + field=models.PositiveIntegerField(blank=True, null=True), + ), + migrations.AddField( + model_name='user', + name='bio', + field=models.TextField(blank=True, max_length=200, null=True), + ), + migrations.AddField( + model_name='user', + name='expirience', + field=models.PositiveIntegerField(blank=True, null=True), + ), + migrations.AddField( + model_name='user', + name='favorite_dicipline', + field=models.TextField(blank=True, max_length=50, null=True), + ), + ] diff --git a/frontend/www/addcontent.html b/frontend/www/addcontent.html new file mode 100644 index 0000000000000000000000000000000000000000..bf6fa17430ae909902ef5ecfc197f14bfd191d50 --- /dev/null +++ b/frontend/www/addcontent.html @@ -0,0 +1,53 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>addcontent</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">Share your workout with the group!</h3> + </div> + </div> + <form class="row g-3" id="form-content"> + <div class="col-lg-6 "> + <label for="inputTitle" class="form-label">Title</label> + <input type="text" class="form-control" id="inputTitle" name="title"> + </div> + <div class="col-lg-6"></div> + <div class="col-lg-6"> + <label for="inputDescription" class="form-label">Description</label> + <textarea class="form-control" id="inputDescription" name="description"></textarea> + </div> + <div class="col-lg-6"> + <div class="col-lg-6"> + <label for="files">Select a file:</label> + <input type="file" class="form-control" id="customFile" name="files" multiple> + </div> + </div> + <div class="col-lg-6"></div> + <div class="col-lg-6"> + <input type="button" class="btn btn-primary" id="btn-ok-addcontent" value=" OK "> + <input type="button" class="btn btn-secondary" id="btn-cancel-addcontent" value="Cancel"> + </div> + <div class="col-lg-6"> + + </div> + </form> + </div> + <script src="scripts/defaults.js"></script> + <script src="scripts/scripts.js"></script> + <script src="scripts/addcontent.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/contentcomments.html b/frontend/www/contentcomments.html new file mode 100644 index 0000000000000000000000000000000000000000..6871cbe0dcdb443a113c88c688379324f87d66c2 --- /dev/null +++ b/frontend/www/contentcomments.html @@ -0,0 +1,69 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Comments</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 text-center"> + <h3 class="mt-5" id="groupName">View comments!</h3> + <p>Here you can post comments and like.</p> + </div> + <div class="col-lg-6"> + <input type="button" class="btn btn-success" id="btn-like" value="Like"> + </div> + <div class="col-lg-6"> + + </div> + <div class="row"> + <div class="col-lg text-center"> + <div class="list-group mt-1" id="div-comment"></div> + </div> + </div> + + </div> + + <template id="template-comment"> + <a class="list-group-item list-group-item-action flex-column align-items-start my-1 exercise" href=""> + <div class="d-flex w-100 justify-content-between align-items-center"> + <h5 class="mb-1"></h5> + </div> + <div class="d-flex"> + <p class="mb-1"> + </p> + </div> + </a> + </template> + + <div class="container"> + <form class="row g-3" id="form-comment"> + <div class="col-lg-6"> + <label for="inputComment" class="form-label">Comment</label> + <textarea class="form-control" id="inputComment" name="comment"></textarea> + </div> + <div class="col-lg-6"></div> + <div class="col-lg-6"> + <input type="button" class="btn btn-primary" id="btn-add-comment" value="Post comment"> + </div> + <div class="col-lg-6"> + + </div> + </form> + </div> + + <script src="scripts/defaults.js"></script> + <script src="scripts/scripts.js"></script> + <script src="scripts/contentcomments.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/groupContent.html b/frontend/www/groupContent.html new file mode 100644 index 0000000000000000000000000000000000000000..5ce93401ff7669eecd37afe1876992b9a1bcde06 --- /dev/null +++ b/frontend/www/groupContent.html @@ -0,0 +1,48 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Groups</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 text-center"> + <h3 class="mt-5" id="groupName">View content!</h3> + <p>Here you can view, create, and edit content.</p> + <input type="button" class="btn btn-primary" id="btn-add-content" value="new Post"> + <input type="button" class="btn btn-link" id="btn-edit-group" value="Edit group details"> + </div> + <div class="row"> + <div class="col-lg text-center"> + <div class="list-group mt-1" id="div-content"></div> + </div> + </div> + </div> + + <template id="template-content"> + <a class="list-group-item list-group-item-action flex-column align-items-start my-1 exercise" href=""> + <div class="d-flex w-100 justify-content-between align-items-center"> + <h5 class="mb-1"></h5> + </div> + <div class="d-flex"> + <p class="mb-1"> + </p> + </div> + </a> + </template> + + <script src="scripts/defaults.js"></script> + <script src="scripts/scripts.js"></script> + <script src="scripts/groupContent.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/addcontent.js b/frontend/www/scripts/addcontent.js new file mode 100644 index 0000000000000000000000000000000000000000..e1e79ab69b37b6f3283df1a99a121b97de14a6cb --- /dev/null +++ b/frontend/www/scripts/addcontent.js @@ -0,0 +1,48 @@ +let cancelButton; +let okButton; + +function handleCancelButtonDuringCreate() { + window.location.replace("groups.html"); +} + +/** + * This api request is fired when the user clicks the button to add content to a group + * It sends a POST request with the form data, and relocates to groupsContent page or + * sends an error message in respons. + */ +async function addContent() { + let form = document.querySelector("#form-content"); + let formData = new FormData(form); + const urlParams = new URLSearchParams(window.location.search); + if (urlParams.has('id')) { + const groupId = urlParams.get('id'); + const input = document.querySelector('input[type="file"]'); + let body = {"group": groupId, + "title": formData.get("title"), + "description": formData.get("description"), + }; + + let response = await sendRequest("POST", `${HOST}/api/content/`, body); + + if (response.ok) { + window.location.replace(`groupContent.html?id=${groupId}`); + } else { + let data = await response.json(); + let alert = createAlert("Could not create new group!", data); + document.body.prepend(alert); + } + } +} + +/** + * 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-addcontent"); + okButton = document.querySelector("#btn-ok-addcontent"); + okButton.addEventListener("click", async () => await addContent()); + cancelButton.addEventListener("click", handleCancelButtonDuringCreate); +}); \ No newline at end of file diff --git a/frontend/www/scripts/contentcomments.js b/frontend/www/scripts/contentcomments.js new file mode 100644 index 0000000000000000000000000000000000000000..fbb45b83475b0b04e43145fc08683bbdd9968f67 --- /dev/null +++ b/frontend/www/scripts/contentcomments.js @@ -0,0 +1,97 @@ +/** + * Sendes a get request to the API to retrieve a list of all the groups + * If the response is ok then each group instance is placed in html code + * to make a list of all the groups. + */ + async function fetchComments(id) { + let response = await sendRequest("GET", `${HOST}/api/comment/${id}`); + + if (response.ok) { + let data = await response.json(); + + let comments = data.results; + let container = document.getElementById('div-comment'); + let commentTemplate = document.querySelector("#template-comment"); + comments.forEach(comment => { + const commentAnchor = commentTemplate.content.firstElementChild.cloneNode(true); + //contentAnchor.href = `group.html?id=${content.id}`; + + const h5 = commentAnchor.querySelector("h5"); + h5.textContent = comment.owner; + + const p = commentAnchor.querySelector("p"); + p.textContent = comment.message; + + container.appendChild(commentAnchor); + }); + } + + return response; +} + +async function addComment() { + let form = document.querySelector("#form-comment"); + let formData = new FormData(form); + const urlParams = new URLSearchParams(window.location.search); + if (urlParams.has('id')) { + const contentId = urlParams.get('id'); + let body = {"content": contentId, + "message": formData.get("comment"), + }; + + let response = await sendRequest("POST", `${HOST}/api/comment/`, body); + + if (response.ok) { + window.location.replace(`contentcomments.html?id=${contentId}`); + } else { + let data = await response.json(); + let alert = createAlert("Could not create new group!", data); + document.body.prepend(alert); + } + } + +} + +async function like() { + const urlParams = new URLSearchParams(window.location.search); + if (urlParams.has('id')) { + const contentId = urlParams.get('id'); + let body = {"content": contentId}; + + let response = await sendRequest("POST", `${HOST}/api/like/`, body); + + if (response.ok) { + window.location.replace(`contentcomments.html?id=${contentId}`); + } else { + let data = await response.json(); + let alert = createAlert("Could not create new group!", data); + document.body.prepend(alert); + } + } + +} + +/** + * When a user enters groups.html then the groups are fetched. + */ +window.addEventListener("DOMContentLoaded", async () => { + let addButton = document.querySelector("#btn-add-comment"); + addButton.addEventListener("click", async () => await addComment()); + let likeButton = document.querySelector("#btn-like"); + likeButton.addEventListener("click", async () => await like()); + + + const urlParams = new URLSearchParams(window.location.search); + + if (urlParams.has('id')) { + const contentId = urlParams.get('id'); + await fetchComments(contentId); + + if (!response.ok) { + let data = await response.json(); + let alert = createAlert("Could not retrieve groups!", data); + document.body.prepend(alert); + } + } + +}); diff --git a/frontend/www/scripts/groupContent.js b/frontend/www/scripts/groupContent.js new file mode 100644 index 0000000000000000000000000000000000000000..08e1c04a6e6c8cc9add3a1be59ffc56b478a6b72 --- /dev/null +++ b/frontend/www/scripts/groupContent.js @@ -0,0 +1,74 @@ +/** + * Sendes a get request to the API to retrieve a list of all the groups + * If the response is ok then each group instance is placed in html code + * to make a list of all the groups. + */ + async function fetchContent(id) { + let response = await sendRequest("GET", `${HOST}/api/content/${id}`); + + if (response.ok) { + let data = await response.json(); + + let contents = data.results; + let container = document.getElementById('div-content'); + let contentTemplate = document.querySelector("#template-content"); + contents.forEach(content => { + const contentAnchor = contentTemplate.content.firstElementChild.cloneNode(true); + contentAnchor.href = `contentcomments.html?id=${content.id}`; + + const h5 = contentAnchor.querySelector("h5"); + h5.textContent = content.title; + + const p = contentAnchor.querySelector("p"); + p.textContent = content.description; + + container.appendChild(contentAnchor); + }); + } + + return response; +} + +function addContent() { + const urlParams = new URLSearchParams(window.location.search); + if (urlParams.has('id')) { + const groupId = urlParams.get('id'); + window.location.replace(`addcontent.html?id=${groupId}`); + } + +} + +function editGroup() { + const urlParams = new URLSearchParams(window.location.search); + if (urlParams.has('id')) { + const groupId = urlParams.get('id'); + window.location.replace(`group.html?id=${groupId}`); + } +} + +/** + * When a user enters groups.html then the groups are fetched. + */ +window.addEventListener("DOMContentLoaded", async () => { + let addButton = document.querySelector("#btn-add-content"); + addButton.addEventListener("click", addContent); + let editButton = document.querySelector("#btn-edit-group"); + editButton.addEventListener("click", editGroup); + + + const urlParams = new URLSearchParams(window.location.search); + + if (urlParams.has('id')) { + const groupId = urlParams.get('id'); + await fetchContent(groupId); + + if (!response.ok) { + let data = await response.json(); + let alert = createAlert("Could not retrieve groups!", data); + document.body.prepend(alert); + } + } + + + +}); diff --git a/frontend/www/scripts/groups.js b/frontend/www/scripts/groups.js index e690c1d0770b235cacd53dffdf333c1bf67a87c9..d3daab52b4676a044a9823840ec5a7c6f7ee3650 100644 --- a/frontend/www/scripts/groups.js +++ b/frontend/www/scripts/groups.js @@ -14,7 +14,7 @@ async function fetchGroups(request) { let groupTemplate = document.querySelector("#template-group"); groups.forEach(group => { const groupAnchor = groupTemplate.content.firstElementChild.cloneNode(true); - groupAnchor.href = `group.html?id=${group.id}`; + groupAnchor.href = `groupContent.html?id=${group.id}`; const h5 = groupAnchor.querySelector("h5"); h5.textContent = group.name;