diff --git a/backend/backend/__pycache__/__init__.cpython-310.pyc b/backend/backend/__pycache__/__init__.cpython-310.pyc index b1daa3a1..a16b9132 100644 Binary files a/backend/backend/__pycache__/__init__.cpython-310.pyc and b/backend/backend/__pycache__/__init__.cpython-310.pyc differ diff --git a/backend/backend/__pycache__/settings.cpython-310.pyc b/backend/backend/__pycache__/settings.cpython-310.pyc index d39715b0..08804844 100644 Binary files a/backend/backend/__pycache__/settings.cpython-310.pyc and b/backend/backend/__pycache__/settings.cpython-310.pyc differ diff --git a/backend/backend/__pycache__/urls.cpython-310.pyc b/backend/backend/__pycache__/urls.cpython-310.pyc index 65621893..53e4f9aa 100644 Binary files a/backend/backend/__pycache__/urls.cpython-310.pyc and b/backend/backend/__pycache__/urls.cpython-310.pyc differ diff --git a/backend/backend/__pycache__/wsgi.cpython-310.pyc b/backend/backend/__pycache__/wsgi.cpython-310.pyc index 1a2733e6..d208bff6 100644 Binary files a/backend/backend/__pycache__/wsgi.cpython-310.pyc and b/backend/backend/__pycache__/wsgi.cpython-310.pyc differ diff --git a/backend/community/__init__.py b/backend/community/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/backend/community/admin.py b/backend/community/admin.py new file mode 100644 index 00000000..8c38f3f3 --- /dev/null +++ b/backend/community/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/backend/community/apps.py b/backend/community/apps.py new file mode 100644 index 00000000..4f527127 --- /dev/null +++ b/backend/community/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class CommunityConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'community' diff --git a/backend/community/authentication.py b/backend/community/authentication.py new file mode 100644 index 00000000..2e7d190d --- /dev/null +++ b/backend/community/authentication.py @@ -0,0 +1,47 @@ +from django.contrib.auth.models import User +from django.conf import settings +from rest_framework import authentication, exceptions +import jwt + +class JWTAuthentication(authentication.BaseAuthentication): + def authenticate(self, request): + auth_header = authentication.get_authorization_header(request).decode('utf-8') + if not auth_header or not auth_header.startswith('Bearer '): + print("No JWT token found in request headers") + return None + + token = auth_header.split(' ')[1] + try: + payload = jwt.decode(token, settings.SUPABASE_SECRET_KEY, algorithms=['HS256'], audience='authenticated') + user_id = payload['sub'] + email = payload.get('email', '') + first_name = payload.get('user_metadata', {}).get('first_name', '') + last_name = payload.get('user_metadata', {}).get('last_name', '') + + # Check if the user exists and update/create accordingly + user, created = User.objects.get_or_create(username=user_id, defaults={ + 'first_name': first_name, + 'last_name': last_name, + 'email': email + }) + + # If the user was not created (i.e., it already exists), update its details + if not created: + user.first_name = first_name + user.last_name = last_name + user.email = email + user.save() + + if created: + print("\nNew user authenticated and created") + else: + print("User authenticated") + + return (user, token) + + except jwt.ExpiredSignatureError: + raise exceptions.AuthenticationFailed('Token expired, login again') + except jwt.InvalidTokenError: + raise exceptions.AuthenticationFailed('Invalid token') + except Exception as e: + raise exceptions.AuthenticationFailed(f'Unexpected error during authentication: {e}') \ No newline at end of file diff --git a/backend/community/db_GetPostData.py b/backend/community/db_GetPostData.py new file mode 100644 index 00000000..96753480 --- /dev/null +++ b/backend/community/db_GetPostData.py @@ -0,0 +1,12 @@ +from supabase import create_client, Client +from django.conf import settings + +def get_supabase_client() -> Client: + url: str = settings.SUPABASE_URL + key: str = settings.SUPABASE_KEY + return create_client(url, key) + +def fetch_post(): + client = get_supabase_client() + data = client.table('posts').select('*').execute() + return data \ No newline at end of file diff --git a/backend/community/migrations/__init__.py b/backend/community/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/backend/community/models.py b/backend/community/models.py new file mode 100644 index 00000000..f89ea80c --- /dev/null +++ b/backend/community/models.py @@ -0,0 +1,14 @@ +from django.db import models +from django.contrib.auth.models import User +from django.utils import timezone # Import timezone + +class Posts(models.Model): + post_id = models.CharField(max_length=255, unique=True) + post_title = models.CharField(max_length=255) + post_content = models.CharField(max_length=255) + user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='postList') + likes = models.IntegerField(default=0) + posted_at = models.DateTimeField(default=timezone.now) # Set default to the current time + + def __str__(self): + return self.title \ No newline at end of file diff --git a/backend/community/tests.py b/backend/community/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/backend/community/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/backend/community/views.py b/backend/community/views.py new file mode 100644 index 00000000..0887bc17 --- /dev/null +++ b/backend/community/views.py @@ -0,0 +1,56 @@ +from django.http import JsonResponse +import json +from django.shortcuts import render +from rest_framework.views import APIView +from rest_framework.permissions import IsAuthenticated +from rest_framework.response import Response +from rest_framework import status +from django.shortcuts import get_object_or_404 +from .db_GetPostData import fetch_post #HERE DO FUNCTIONS IG. +from .models import Posts +from django.contrib.auth.models import User + +#def post(request): + # all_posts = Posts.objects.all + # return render(request, '../../web/src/components/post.js') + +class TaskListCreate(APIView): + permission_classes = [IsAuthenticated] + + def get(self, request): + user_uuid = request.user.username + + try: + response = fetch_post() + postList = [] + + if response.data: + for post_data in response.data: + user, _ = User.objects.get_or_create(username=post_data['post_id']) + post = Posts.objects.update_or_create( + post_id = post_data['post_id'], + defaults={ + 'post_title': post_data['post_title'], + 'posted_at': post_data['posted_at'], + 'post_content': post_data['post_content'], + 'likes':post_data['likes'], + 'user': user, + } + ) + + postList.append({ + 'post_id': post.post_id, + 'post_title': post.post_title, + 'post_content': post.post_content, + 'likes': post.likes, + 'posted_at': post.posted_at + }) + + print(len(postList), "tasks found") + + return JsonResponse({'tasks': postList}, safe=False) + + except Exception as e: + print("Error syncing tasks") + print(e) + return JsonResponse({'error': 'Failed to fetch and sync tasks.'}, status=500) \ No newline at end of file diff --git a/backend/to_do_list/__pycache__/__init__.cpython-310.pyc b/backend/to_do_list/__pycache__/__init__.cpython-310.pyc index a4697906..19aefa09 100644 Binary files a/backend/to_do_list/__pycache__/__init__.cpython-310.pyc and b/backend/to_do_list/__pycache__/__init__.cpython-310.pyc differ diff --git a/backend/to_do_list/__pycache__/apps.cpython-310.pyc b/backend/to_do_list/__pycache__/apps.cpython-310.pyc index 14917a8a..aa1f8098 100644 Binary files a/backend/to_do_list/__pycache__/apps.cpython-310.pyc and b/backend/to_do_list/__pycache__/apps.cpython-310.pyc differ diff --git a/backend/to_do_list/__pycache__/authentication.cpython-310.pyc b/backend/to_do_list/__pycache__/authentication.cpython-310.pyc index 2dd14bc9..b895a753 100644 Binary files a/backend/to_do_list/__pycache__/authentication.cpython-310.pyc and b/backend/to_do_list/__pycache__/authentication.cpython-310.pyc differ diff --git a/backend/to_do_list/__pycache__/db_service.cpython-310.pyc b/backend/to_do_list/__pycache__/db_service.cpython-310.pyc index 749f50e6..3bdd4459 100644 Binary files a/backend/to_do_list/__pycache__/db_service.cpython-310.pyc and b/backend/to_do_list/__pycache__/db_service.cpython-310.pyc differ diff --git a/backend/to_do_list/__pycache__/models.cpython-310.pyc b/backend/to_do_list/__pycache__/models.cpython-310.pyc index 69c86add..b8988d21 100644 Binary files a/backend/to_do_list/__pycache__/models.cpython-310.pyc and b/backend/to_do_list/__pycache__/models.cpython-310.pyc differ diff --git a/backend/to_do_list/__pycache__/urls.cpython-310.pyc b/backend/to_do_list/__pycache__/urls.cpython-310.pyc index a6fd82f3..f93a6168 100644 Binary files a/backend/to_do_list/__pycache__/urls.cpython-310.pyc and b/backend/to_do_list/__pycache__/urls.cpython-310.pyc differ diff --git a/backend/to_do_list/__pycache__/views.cpython-310.pyc b/backend/to_do_list/__pycache__/views.cpython-310.pyc index 45130b8e..14325186 100644 Binary files a/backend/to_do_list/__pycache__/views.cpython-310.pyc and b/backend/to_do_list/__pycache__/views.cpython-310.pyc differ diff --git a/backend/to_do_list/migrations/__pycache__/0001_initial.cpython-310.pyc b/backend/to_do_list/migrations/__pycache__/0001_initial.cpython-310.pyc index f3e3e82b..db9ebe34 100644 Binary files a/backend/to_do_list/migrations/__pycache__/0001_initial.cpython-310.pyc and b/backend/to_do_list/migrations/__pycache__/0001_initial.cpython-310.pyc differ diff --git a/backend/to_do_list/migrations/__pycache__/0002_task_task_id_task_title_alter_task_user.cpython-310.pyc b/backend/to_do_list/migrations/__pycache__/0002_task_task_id_task_title_alter_task_user.cpython-310.pyc new file mode 100644 index 00000000..a037f731 Binary files /dev/null and b/backend/to_do_list/migrations/__pycache__/0002_task_task_id_task_title_alter_task_user.cpython-310.pyc differ diff --git a/backend/to_do_list/migrations/__pycache__/0003_task_created_at.cpython-310.pyc b/backend/to_do_list/migrations/__pycache__/0003_task_created_at.cpython-310.pyc new file mode 100644 index 00000000..b3780d0e Binary files /dev/null and b/backend/to_do_list/migrations/__pycache__/0003_task_created_at.cpython-310.pyc differ diff --git a/backend/to_do_list/migrations/__pycache__/__init__.cpython-310.pyc b/backend/to_do_list/migrations/__pycache__/__init__.cpython-310.pyc index 1098992d..fd40f9b6 100644 Binary files a/backend/to_do_list/migrations/__pycache__/__init__.cpython-310.pyc and b/backend/to_do_list/migrations/__pycache__/__init__.cpython-310.pyc differ diff --git a/web/src/components/post.js b/web/src/components/post.js index fb22690f..cc538f89 100644 --- a/web/src/components/post.js +++ b/web/src/components/post.js @@ -1,11 +1,67 @@ -import React from 'react' +import React, {useState, useEffect, useCallback} from "react"; import "../css/post.css" -function post() { +import UserIcon from "../assets/images/UserIcon.png" +function Post() { + const [postList, setPosts] = useState([]); + + const fetchPosts = useCallback(async () => { + const response = await fetch('/community/postList', { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }); + const data = await response.json(); + if (data && data.postList) { + setPosts(data.postList); + } else { + // Handle any errors or empty responses + console.error('Failed to fetch tasks or no tasks available'); + } + }, ); + useEffect(() => { + fetchPosts(); + }, [fetchPosts]); + + + + const [liked, setLike] = useState(false); + + const handleLike = () => { + setLike(!liked); + }; + + const [flagged, setFlag] = useState(false); + + const handleFlag = () => { + setFlag(!flagged); + }; return ( -
- +
+ + + {postList.map((postList, index) => ( +
+ {/*Insert users profile picture here */}
+
+ {/*Insert post title here */}

{postList.post_title}

+

{postList.post_content}

+
+ {/*Insert users Name here */}

{postList.user}

+ + + {/*Insert date posted here */}

{postList.posted_at}

+
+ ))} + {/*
*/} + + + +
) } -export default post +export default Post diff --git a/web/src/components/postspare.js b/web/src/components/postspare.js new file mode 100644 index 00000000..a4da8c6f --- /dev/null +++ b/web/src/components/postspare.js @@ -0,0 +1,33 @@ +import React, { useState } from 'react'; +import "../css/post.css" +import UserIcon from "../assets/images/UserIcon.png" +function Post() { + const [liked, setLike] = useState(false); + + const handleLike = () => { + setLike(!liked); + }; + + const [flagged, setFlag] = useState(false); + + const handleFlag = () => { + setFlag(!flagged); + }; + return ( +
+ {/*Insert users profile picture here */}
+
+ {/*Insert post title here */}

Happiness

+

dgsi fodj sdjfids jiofnioa hg iohparuiogbuah bpaoiidfsn nje fnsjfknsndfj nsk fnsn fdk slnfd kskd fnsanfdknas kdfna dfknasdkf nsdanf ksadn fknas dkfnask nfdksnf kdnsafkl nas;lf nads;n fdas nfksadn fn;asdnf;aldfn;ansfd ndnfasndfla ndfln asnf asnfd ;lasnf d;nasdfk; adfdklasn f;las flk;andsfkl nasdnf kas f;ldnaks nfd;ak nsflkdan s;kfldn aklsdnf ;asdn fl;aks nf u phg iowah nuifp hnaw uibhefubqwu brigf ehrifje hgbrgui bg uihqe ugta ehig prqhuiri hh rp ibaeur buiarh angrjg nabgur aug boib g oa

+
+ {/*Insert users Name here */}

Jane

+ + + {/*Insert date posted here */}

01/04/24

+
+ ) +} + +export default Post \ No newline at end of file diff --git a/web/src/css/community.css b/web/src/css/community.css index 75eecff5..f6724d5b 100644 --- a/web/src/css/community.css +++ b/web/src/css/community.css @@ -55,6 +55,9 @@ border-radius: 140px; background: rgba(143, 143, 143, 0.50); box-shadow: 2px 4px 4px 0px rgba(0, 0, 0, 0.25); + display: flex; + flex-direction: column; + align-items: center; } .Community .postsWrapper::-webkit-scrollbar { display: none; /* Hide scrollbar */ @@ -63,4 +66,29 @@ margin-top: 2vh; margin-left: 15vw; width: 50vw; +} + +.Community .modal { + display: none; + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + padding: 20px; + background-color: #fff; + border-radius: 8px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); + z-index: 1000; +} + +/* Style for the overlay/background behind the modal */ +.Community .overlay { + display: none; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + z-index: 900; } \ No newline at end of file diff --git a/web/src/css/post.css b/web/src/css/post.css index e69de29b..0528d216 100644 --- a/web/src/css/post.css +++ b/web/src/css/post.css @@ -0,0 +1,85 @@ +.PostHolder { + border-radius: 33px; + border: 2px solid #FF004F; + background: rgba(255, 255, 255, 0.80); + width: 50vw; + height: 13vh; + margin-top: 2vh; + padding: 10px; + display: flex; + flex-direction: row; + color: black; +} + +.PostHolder .imgHolder { + width: 10vh; /* Make width and height equal for square shape */ + height: 10vh; + border-radius: 4px; + border: 3px solid #FF004F; + background: #D9D9D9; + margin-top: 0.6vh; + margin-left: 0.2vw; +} + +.PostHolder .imgHolder img { + max-width: 100%; /* Make the image fill the container horizontally */ + max-height: 100%; /* Make the image fill the container vertically */ +} + +.PostHolder .Postinfo { + height: 100%; + display: flex; + flex-direction: column; + width: 75%; + word-wrap: break-word; + font-size: 15px; + font-style: normal; + font-weight: 400; + line-height: 114.583%; /* 22.917px */ + margin-top: 1vh; + margin-left: 0.5vw; + +} + +.PostHolder .PostTitle{ + text-shadow: none; + font-size: 40px; + font-style: normal; + font-weight: 500; + letter-spacing: 2.8px; + + text-align: left; +} +.PostHolder .infoContent{ + margin-top: 1vh; + overflow-y: hidden; + display: flex; + flex-direction: row; +} + +.PostHolder .likeButton { + background-color: grey; /* Change to your desired color */ + width: 3vh; /* Make width and height equal for square shape */ + height: 3vh; + border-radius: 50px; + margin-top: 8vh; +} + +.PostHolder .likeButton.clicked { + background-color: red; /* Change to your desired color */ +} + +.PostHolder .Name{ + margin-left: -3vw; + font-size: 22px; +} +.PostHolder .flag{ + margin-top: 3.5vh; + background-color: rgb(212, 212, 212); /* Change to your desired color */ + width: 3vh; /* Make width and height equal for square shape */ + height: 3vh; + border-radius: 50px; +} +.PostHolder .flag.clicked { + background-color: rgb(2, 81, 110); /* Change to your desired color */ +} \ No newline at end of file diff --git a/web/src/pages/Community.js b/web/src/pages/Community.js index 233bbd1e..d1f20dbe 100644 --- a/web/src/pages/Community.js +++ b/web/src/pages/Community.js @@ -1,14 +1,40 @@ -import React from 'react' +import React, { useState } from 'react'; import Navbar from "../components/Navbar"; import "../css/community.css" import filter from "../assets/images/filter icon.png" import Line from "../assets/images/Line.png" -import post from "../assets/images/post.png" +import Post from "../components/post" + function Community() { + + const [showModal, setShowModal] = useState(false); + + const openModal = () => { + setShowModal(true); + }; + + const closeModal = () => { + setShowModal(false); + }; + + const handleOverlayClick = () => { + closeModal(); + }; + + + return (
- + + {showModal && ( +
+

add post

+ {/* Insert add post POST form here*/} +
+ )} + {showModal &&
} +
@@ -20,16 +46,9 @@ function Community() {
{/* Make into components instead of img maybe or cld be like this as just prototype */} - - - - - - - - - - + + +
)