From d90ebec2ab2d980768cafd81fc37e266b24c4f61 Mon Sep 17 00:00:00 2001 From: Yami Naik Date: Mon, 23 Oct 2023 00:03:45 -0400 Subject: [PATCH 01/16] Signup Login API --- furbaby/api/auth_backends.py | 28 +++++++++++++ furbaby/api/migrations/0002_initial.py | 24 +++-------- ...s_options_alter_users_managers_and_more.py | 4 +- .../migrations/0008_alter_users_user_type.py | 24 +++++++++++ furbaby/api/models.py | 21 ++++------ furbaby/api/serializers.py | 42 +++++++++++++++++++ furbaby/api/urls.py | 14 ++++++- furbaby/api/views.py | 39 ++++++++++++++++- furbaby/furbaby/local_settings.py | 3 +- furbaby/furbaby/settings.py | 1 + 10 files changed, 164 insertions(+), 36 deletions(-) create mode 100644 furbaby/api/auth_backends.py create mode 100644 furbaby/api/migrations/0008_alter_users_user_type.py create mode 100644 furbaby/api/serializers.py diff --git a/furbaby/api/auth_backends.py b/furbaby/api/auth_backends.py new file mode 100644 index 0000000..6ffaef4 --- /dev/null +++ b/furbaby/api/auth_backends.py @@ -0,0 +1,28 @@ +from django.contrib.auth import get_user_model +from django.contrib.auth.backends import ModelBackend +from django.contrib.auth.hashers import check_password + + +class EmailBackend(ModelBackend): + def authenticate(self, request, email=None, password=None, **kwargs): + User = get_user_model() + try: + user = User.objects.get(email=email) + print(user) + print(password) + print(user.password) + if check_password(password, user.password): + print("From backend", user) + return user + else: + print("Something wrong") + except User.DoesNotExist: + print("From backend") + return None + + def get_user(self, user_id): + User = get_user_model() + try: + return User.objects.get(pk=user_id) + except User.DoesNotExist: + return None diff --git a/furbaby/api/migrations/0002_initial.py b/furbaby/api/migrations/0002_initial.py index becffe0..fc953e3 100644 --- a/furbaby/api/migrations/0002_initial.py +++ b/furbaby/api/migrations/0002_initial.py @@ -78,9 +78,7 @@ class Migration(migrations.Migration): ("updated_at", models.DateTimeField(auto_now=True)), ( "owner_id", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, to="api.users" - ), + models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="api.users"), ), ], ), @@ -104,9 +102,7 @@ class Migration(migrations.Migration): ("updated_at", models.DateTimeField(auto_now=True)), ( "user_id", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, to="api.users" - ), + models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="api.users"), ), ], ), @@ -136,15 +132,11 @@ class Migration(migrations.Migration): ), ( "pet_id", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, to="api.pets" - ), + models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="api.pets"), ), ( "user_id", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, to="api.users" - ), + models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="api.users"), ), ], ), @@ -166,15 +158,11 @@ class Migration(migrations.Migration): ("updated_at", models.DateTimeField(auto_now=True)), ( "job_id", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, to="api.jobs" - ), + models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="api.jobs"), ), ( "user_id", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, to="api.users" - ), + models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="api.users"), ), ], ), diff --git a/furbaby/api/migrations/0006_alter_users_options_alter_users_managers_and_more.py b/furbaby/api/migrations/0006_alter_users_options_alter_users_managers_and_more.py index 4c5f199..85e24a3 100644 --- a/furbaby/api/migrations/0006_alter_users_options_alter_users_managers_and_more.py +++ b/furbaby/api/migrations/0006_alter_users_options_alter_users_managers_and_more.py @@ -72,9 +72,7 @@ class Migration(migrations.Migration): migrations.AddField( model_name="users", name="last_login", - field=models.DateTimeField( - blank=True, null=True, verbose_name="last login" - ), + field=models.DateTimeField(blank=True, null=True, verbose_name="last login"), ), migrations.AddField( model_name="users", diff --git a/furbaby/api/migrations/0008_alter_users_user_type.py b/furbaby/api/migrations/0008_alter_users_user_type.py new file mode 100644 index 0000000..dd57f16 --- /dev/null +++ b/furbaby/api/migrations/0008_alter_users_user_type.py @@ -0,0 +1,24 @@ +# Generated by Django 4.0 on 2023-10-22 16:44 + +import django.contrib.postgres.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("api", "0007_applications_user_id_job_id_constraint_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="users", + name="user_type", + field=django.contrib.postgres.fields.ArrayField( + base_field=models.CharField( + choices=[("Pet Sitter", "Pet Sitter"), ("Pet Owner", "Pet Owner")], + max_length=20, + ), + size=2, + ), + ), + ] diff --git a/furbaby/api/models.py b/furbaby/api/models.py index fa3490f..d0f8c71 100644 --- a/furbaby/api/models.py +++ b/furbaby/api/models.py @@ -29,6 +29,11 @@ """ +class ChoiceEnum(models.TextChoices): + PET_SITTER = "Pet Sitter" + PET_OWNER = "Pet Owner" + + class Users(AbstractUser): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) email = models.TextField(max_length=200, null=False, editable=True, unique=True) @@ -36,12 +41,8 @@ class Users(AbstractUser): first_name = models.TextField(null=False, editable=True) last_name = models.TextField(null=False, editable=True) user_type = ArrayField( - models.TextField(null=True, blank=True), - blank=True, - size=2, - null=False, - editable=True, - ) + models.CharField(max_length=20, choices=ChoiceEnum.choices), size=2 + ) # Define choices here profile_picture = models.TextField(editable=True) date_of_birth = models.DateField(editable=False) experience = models.TextField(editable=True) @@ -133,9 +134,7 @@ class Pets(models.Model): class Meta: constraints = [ - models.UniqueConstraint( - fields=("name", "owner_id"), name="name_owner_id_constraint" - ) + models.UniqueConstraint(fields=("name", "owner_id"), name="name_owner_id_constraint") ] @@ -220,7 +219,5 @@ class Applications(models.Model): class Meta: constraints = [ - models.UniqueConstraint( - fields=("user_id", "job_id"), name="user_id_job_id_constraint" - ) + models.UniqueConstraint(fields=("user_id", "job_id"), name="user_id_job_id_constraint") ] diff --git a/furbaby/api/serializers.py b/furbaby/api/serializers.py new file mode 100644 index 0000000..8f9ee58 --- /dev/null +++ b/furbaby/api/serializers.py @@ -0,0 +1,42 @@ +from rest_framework import serializers +from .models import Users +from django.contrib.auth.hashers import make_password +from django.core.exceptions import ValidationError + + +class UserRegistrationSerializer(serializers.ModelSerializer): + user_type = serializers.ListField(child=serializers.CharField(max_length=20), write_only=True) + date_of_birth = serializers.DateField() + + class Meta: + model = Users + fields = [ + "email", + "password", + "first_name", + "last_name", + "experience", + "date_of_birth", + "qualifications", + "user_type", + ] + + def validate(self, data): + user_type = data.get("user_type", []) + email = data.get("email", "") + + if "Pet Sitter" in user_type and not email.endswith("nyu.edu"): + raise ValidationError("Pet sitters must have a nyu.edu email") + return data + + def create(self, validated_data): + # Hash the password + validated_data["password"] = make_password(validated_data["password"]) + + # Create and return the user + return Users.objects.create(**validated_data) + + +class UserLoginSerializer(serializers.Serializer): + email = serializers.EmailField() + password = serializers.CharField() diff --git a/furbaby/api/urls.py b/furbaby/api/urls.py index 7ece5c9..7785e42 100644 --- a/furbaby/api/urls.py +++ b/furbaby/api/urls.py @@ -1,5 +1,17 @@ from django.urls import path +from django.db import models +from django.contrib.postgres.fields import ArrayField +from django.contrib.auth.models import AbstractUser + +# from django.contrib.auth.hashers import make_password use this while storing use passwords +import uuid from . import views +from .views import UserRegistrationView +from .views import UserLoginView -urlpatterns = [path("", views.index, name="index")] +urlpatterns = [ + path("", views.index, name="index"), + path("register/", UserRegistrationView.as_view(), name="user-registration"), + path("login/", UserLoginView.as_view(), name="user-login"), +] diff --git a/furbaby/api/views.py b/furbaby/api/views.py index d2099f1..565108f 100644 --- a/furbaby/api/views.py +++ b/furbaby/api/views.py @@ -1,5 +1,42 @@ -from django.shortcuts import render from django.http import HttpResponse +from rest_framework.generics import CreateAPIView +from rest_framework.views import APIView +from rest_framework.response import Response +from rest_framework import status +from django.contrib.auth import login +from .serializers import UserRegistrationSerializer, UserLoginSerializer +from api.auth_backends import EmailBackend + + +class UserRegistrationView(CreateAPIView): + serializer_class = UserRegistrationSerializer + + +class UserLoginView(APIView): + def post(self, request): + serializer = UserLoginSerializer(data=request.data) + if serializer.is_valid(): + print(serializer.data) + email = serializer.validated_data["email"] + password = serializer.validated_data["password"] + email_backend = EmailBackend() + user = email_backend.authenticate(request, email=email, password=password) + print(user) + if user is not None: + print(f"User found: {user.username}") + login(request, user) + return Response({"message": "Login successful"}, status=status.HTTP_200_OK) + else: + print(f"Invalid login attempt: {serializer.validated_data['email']}") + return Response( + {"message": "Invalid credentials"}, status=status.HTTP_401_UNAUTHORIZED + ) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + +def index(req): + return HttpResponse("Hello, world", status=200) + # Create your views here. diff --git a/furbaby/furbaby/local_settings.py b/furbaby/furbaby/local_settings.py index 3753fa7..1979144 100644 --- a/furbaby/furbaby/local_settings.py +++ b/furbaby/furbaby/local_settings.py @@ -11,11 +11,11 @@ """ from pathlib import Path -import os from dotenv import load_dotenv load_dotenv() + # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent @@ -97,6 +97,7 @@ AUTH_USER_MODEL = "api.Users" +AUTHENTICATION_BACKENDS = ["api.auth_backends.EmailBackend"] # Password validation # https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators diff --git a/furbaby/furbaby/settings.py b/furbaby/furbaby/settings.py index 5de4a2e..3025cbf 100644 --- a/furbaby/furbaby/settings.py +++ b/furbaby/furbaby/settings.py @@ -97,6 +97,7 @@ AUTH_USER_MODEL = "api.Users" +AUTHENTICATION_BACKENDS = ["api.auth_backends.EmailBackend"] # Password validation # https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators From 87fceddaa11685bf0ab1648f611b4f5a3782a48f Mon Sep 17 00:00:00 2001 From: Yami Naik Date: Mon, 23 Oct 2023 00:07:45 -0400 Subject: [PATCH 02/16] Signup Login API --- furbaby/requirements.txt | 66 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/furbaby/requirements.txt b/furbaby/requirements.txt index 51f5228..0b9cefb 100644 --- a/furbaby/requirements.txt +++ b/furbaby/requirements.txt @@ -1,27 +1,89 @@ asgiref==3.7.2 astroid==2.15.8 +attrs==22.2.0 +awsebcli==3.20.10 backports.zoneinfo==0.2.1 +beautifulsoup4==4.11.2 black==23.9.1 +bleach==6.0.0 +blessed==1.20.0 +botocore==1.31.64 +cement==2.8.2 +certifi==2023.7.22 +charset-normalizer==3.3.0 click==8.1.7 +colorama==0.4.3 +coverage==6.5.0 +coveralls==3.3.1 +defusedxml==0.7.1 dill==0.3.7 +distlib==0.3.7 Django==4.0 +django-cors-headers==4.3.0 django-heartbeat==2.2.0 +djangorestframework==3.14.0 +docopt==0.6.2 +exceptiongroup==1.1.3 +fastjsonschema==2.16.2 +filelock==3.12.4 +flake8==5.0.4 +idna==3.4 +importlib-metadata==6.0.0 +importlib-resources==5.10.2 +iniconfig==2.0.0 isort==5.12.0 +Jinja2==3.1.2 +jmespath==1.0.1 +jsonschema==4.17.3 +jupyter_client==8.0.2 +jupyter_core==5.2.0 +jupyterlab-pygments==0.2.2 lazy-object-proxy==1.9.0 +MarkupSafe==2.1.2 mccabe==0.7.0 +mistune==2.0.5 mypy-extensions==1.0.0 +nbclient==0.7.2 +nbconvert==7.2.9 +nbformat==5.7.3 packaging==23.2 -pathspec==0.11.2 +pandocfilters==1.5.0 +pathspec==0.10.1 +Pillow==10.1.0 +pkgutil_resolve_name==1.3.10 platformdirs==3.11.0 +pluggy==1.3.0 psutil==5.9.5 psycopg==3.1.12 psycopg2-binary==2.9.9 +pycodestyle==2.9.1 +pyflakes==2.5.0 +Pygments==2.14.0 pylint==2.17.7 pylint-django==2.5.3 pylint-plugin-utils==0.8.2 +pyrsistent==0.19.3 +pytest==7.4.2 +python-dateutil==2.8.2 python-dotenv==1.0.0 +pytz==2023.3.post1 +PyYAML==6.0.1 +pyzmq==25.0.0 +requests==2.31.0 +semantic-version==2.8.5 +six==1.16.0 +soupsieve==2.4 sqlparse==0.4.4 +termcolor==1.1.0 +tinycss2==1.2.1 tomli==2.0.1 tomlkit==0.12.1 -typing-extensions==4.8.0 +tornado==6.2 +traitlets==5.9.0 +typing_extensions==4.8.0 +urllib3==1.26.17 +virtualenv==20.24.5 +wcwidth==0.1.9 +webencodings==0.5.1 wrapt==1.15.0 +zipp==3.13.0 From 1c74a4e956aaf7f4ad8d42a49ee560f6fbe0fa16 Mon Sep 17 00:00:00 2001 From: Yami Naik Date: Mon, 23 Oct 2023 00:27:18 -0400 Subject: [PATCH 03/16] Signup Login API --- furbaby/api/auth_backends.py | 8 +++----- furbaby/api/views.py | 2 -- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/furbaby/api/auth_backends.py b/furbaby/api/auth_backends.py index 6ffaef4..cd3f184 100644 --- a/furbaby/api/auth_backends.py +++ b/furbaby/api/auth_backends.py @@ -1,6 +1,7 @@ from django.contrib.auth import get_user_model from django.contrib.auth.backends import ModelBackend from django.contrib.auth.hashers import check_password +from django.http import HttpResponse class EmailBackend(ModelBackend): @@ -12,13 +13,10 @@ def authenticate(self, request, email=None, password=None, **kwargs): print(password) print(user.password) if check_password(password, user.password): - print("From backend", user) return user - else: - print("Something wrong") except User.DoesNotExist: - print("From backend") - return None + return HttpResponse("User Not Found", status=404) + def get_user(self, user_id): User = get_user_model() diff --git a/furbaby/api/views.py b/furbaby/api/views.py index 565108f..973b5a0 100644 --- a/furbaby/api/views.py +++ b/furbaby/api/views.py @@ -21,9 +21,7 @@ def post(self, request): password = serializer.validated_data["password"] email_backend = EmailBackend() user = email_backend.authenticate(request, email=email, password=password) - print(user) if user is not None: - print(f"User found: {user.username}") login(request, user) return Response({"message": "Login successful"}, status=status.HTTP_200_OK) else: From 2537c546b2516ba31cc7cecd531a47cf2f5df993 Mon Sep 17 00:00:00 2001 From: Yami Naik Date: Mon, 23 Oct 2023 18:55:43 -0400 Subject: [PATCH 04/16] Signup Api Modification --- furbaby/api/serializers.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/furbaby/api/serializers.py b/furbaby/api/serializers.py index 8f9ee58..ec248e3 100644 --- a/furbaby/api/serializers.py +++ b/furbaby/api/serializers.py @@ -12,12 +12,9 @@ class Meta: model = Users fields = [ "email", + "username", "password", - "first_name", - "last_name", - "experience", "date_of_birth", - "qualifications", "user_type", ] @@ -40,3 +37,4 @@ def create(self, validated_data): class UserLoginSerializer(serializers.Serializer): email = serializers.EmailField() password = serializers.CharField() + \ No newline at end of file From ab82def06e710e9ce2c10a9bf3bd2fa65beaa49d Mon Sep 17 00:00:00 2001 From: Yami Naik Date: Thu, 26 Oct 2023 11:47:20 -0400 Subject: [PATCH 05/16] Pet View/Edit Profile --- furbaby/api/auth_backends.py | 2 +- furbaby/api/serializers.py | 12 +++++++++--- furbaby/api/urls.py | 6 +++++- furbaby/api/views.py | 28 +++++++++++++++++++--------- 4 files changed, 34 insertions(+), 14 deletions(-) diff --git a/furbaby/api/auth_backends.py b/furbaby/api/auth_backends.py index cd3f184..afe88a4 100644 --- a/furbaby/api/auth_backends.py +++ b/furbaby/api/auth_backends.py @@ -15,7 +15,7 @@ def authenticate(self, request, email=None, password=None, **kwargs): if check_password(password, user.password): return user except User.DoesNotExist: - return HttpResponse("User Not Found", status=404) + return None def get_user(self, user_id): diff --git a/furbaby/api/serializers.py b/furbaby/api/serializers.py index ec248e3..6e9f5a8 100644 --- a/furbaby/api/serializers.py +++ b/furbaby/api/serializers.py @@ -1,8 +1,8 @@ from rest_framework import serializers -from .models import Users +from .models import Users,Pets from django.contrib.auth.hashers import make_password from django.core.exceptions import ValidationError - +from django.contrib.auth import get_user_model class UserRegistrationSerializer(serializers.ModelSerializer): user_type = serializers.ListField(child=serializers.CharField(max_length=20), write_only=True) @@ -37,4 +37,10 @@ def create(self, validated_data): class UserLoginSerializer(serializers.Serializer): email = serializers.EmailField() password = serializers.CharField() - \ No newline at end of file + +class PetSerializer(serializers.ModelSerializer): + owner = serializers.HiddenField(default=serializers.CurrentUserDefault()) + + class Meta: + model = Pets + exclude = () \ No newline at end of file diff --git a/furbaby/api/urls.py b/furbaby/api/urls.py index 7785e42..32ee339 100644 --- a/furbaby/api/urls.py +++ b/furbaby/api/urls.py @@ -8,10 +8,14 @@ from . import views from .views import UserRegistrationView -from .views import UserLoginView +from .views import UserLoginView,PetListCreateView,PetRetrieveUpdateDeleteView + urlpatterns = [ path("", views.index, name="index"), path("register/", UserRegistrationView.as_view(), name="user-registration"), path("login/", UserLoginView.as_view(), name="user-login"), + path('pets/', PetListCreateView.as_view(), name='pet-list-create'), + path('pets//', PetRetrieveUpdateDeleteView.as_view(), name='pet-retrieve-update-delete'), + ] diff --git a/furbaby/api/views.py b/furbaby/api/views.py index 973b5a0..7a7ea66 100644 --- a/furbaby/api/views.py +++ b/furbaby/api/views.py @@ -1,12 +1,13 @@ from django.http import HttpResponse -from rest_framework.generics import CreateAPIView +from rest_framework.generics import CreateAPIView,RetrieveAPIView,RetrieveUpdateAPIView,ListCreateAPIView,RetrieveUpdateDestroyAPIView from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status from django.contrib.auth import login -from .serializers import UserRegistrationSerializer, UserLoginSerializer +from .serializers import UserRegistrationSerializer, UserLoginSerializer,PetSerializer from api.auth_backends import EmailBackend - +from rest_framework.permissions import IsAuthenticated +from .models import Pets,Locations class UserRegistrationView(CreateAPIView): serializer_class = UserRegistrationSerializer @@ -31,13 +32,22 @@ def post(self, request): ) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - +class PetListCreateView(ListCreateAPIView): + queryset = Pets.objects.all() + serializer_class = PetSerializer + permission_classes = [IsAuthenticated] + + def create(self, request, *args, **kwargs): + # Set the owner to the authenticated user + request.data["owner_id"] = request.user.id + return super().create(request, *args, **kwargs) + +class PetRetrieveUpdateDeleteView(RetrieveUpdateDestroyAPIView): + queryset = Pets.objects.all() + serializer_class = PetSerializer + permission_classes = [IsAuthenticated] + def index(req): return HttpResponse("Hello, world", status=200) -# Create your views here. - - -def index(req): - return HttpResponse("Hello, world", status=200) From df343f161cdcd6018654d281dd4c6c88ae0ae327 Mon Sep 17 00:00:00 2001 From: Yami Naik Date: Sun, 29 Oct 2023 16:38:00 -0400 Subject: [PATCH 06/16] Solved minor issue --- furbaby/api/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/furbaby/api/serializers.py b/furbaby/api/serializers.py index d20477a..2ff2c3c 100644 --- a/furbaby/api/serializers.py +++ b/furbaby/api/serializers.py @@ -1,5 +1,5 @@ from rest_framework import serializers -from .models import Users +from .models import Users,Pets from django.contrib.auth.hashers import make_password from django.core.exceptions import ValidationError From 1919f211c1740809e66dae4351c5b4e3fa1c5c2b Mon Sep 17 00:00:00 2001 From: Yami Naik Date: Sun, 29 Oct 2023 16:44:01 -0400 Subject: [PATCH 07/16] minor import change --- furbaby/api/views.py | 6 ++++-- furbaby/furbaby/local_settings.py | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/furbaby/api/views.py b/furbaby/api/views.py index 0e19b85..ba12366 100644 --- a/furbaby/api/views.py +++ b/furbaby/api/views.py @@ -6,11 +6,13 @@ from rest_framework.views import APIView from rest_framework.generics import GenericAPIView from django.views.decorators.csrf import ensure_csrf_cookie +from rest_framework.generics import ListCreateAPIView,RetrieveUpdateDestroyAPIView +from rest_framework.permissions import IsAuthenticated from .utils import json_response from api.auth_backends import EmailBackend -from .serializers import RegistrationSerializer, UserLoginSerializer - +from .serializers import RegistrationSerializer, UserLoginSerializer, PetSerializer +from .models import Pets from django.core.mail import EmailMultiAlternatives from django.dispatch import receiver from django.template.loader import render_to_string diff --git a/furbaby/furbaby/local_settings.py b/furbaby/furbaby/local_settings.py index 54dc20c..12880ac 100644 --- a/furbaby/furbaby/local_settings.py +++ b/furbaby/furbaby/local_settings.py @@ -12,6 +12,7 @@ from pathlib import Path from dotenv import load_dotenv +import os os.environ.setdefault("FORGOT_PASSWORD_HOST", "http://localhost:3000") From 99bb2e6f37dc81f002645b8e91c96a9d4ea34338 Mon Sep 17 00:00:00 2001 From: Yami Naik Date: Mon, 6 Nov 2023 12:53:35 -0500 Subject: [PATCH 08/16] Add import statement --- furbaby/api/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/furbaby/api/views.py b/furbaby/api/views.py index ba12366..e80b856 100644 --- a/furbaby/api/views.py +++ b/furbaby/api/views.py @@ -8,7 +8,7 @@ from django.views.decorators.csrf import ensure_csrf_cookie from rest_framework.generics import ListCreateAPIView,RetrieveUpdateDestroyAPIView from rest_framework.permissions import IsAuthenticated - +from rest_framework.response import Response from .utils import json_response from api.auth_backends import EmailBackend from .serializers import RegistrationSerializer, UserLoginSerializer, PetSerializer From e1454debe562c22157eab36669da0576aeef15c5 Mon Sep 17 00:00:00 2001 From: Yami Naik Date: Mon, 6 Nov 2023 13:21:19 -0500 Subject: [PATCH 09/16] solved build error --- furbaby/api/serializers.py | 7 ++++--- furbaby/api/urls.py | 8 +++++--- furbaby/api/views.py | 7 +++++-- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/furbaby/api/serializers.py b/furbaby/api/serializers.py index eb2bcdd..759ef9a 100644 --- a/furbaby/api/serializers.py +++ b/furbaby/api/serializers.py @@ -1,5 +1,5 @@ from rest_framework import serializers -from .models import Users,Pets +from .models import Users, Pets from django.contrib.auth.hashers import make_password from django.core.exceptions import ValidationError @@ -46,9 +46,10 @@ class UserLoginSerializer(serializers.Serializer): email = serializers.EmailField() password = serializers.CharField(min_length=8, write_only=True) + class PetSerializer(serializers.ModelSerializer): - owner = serializers.HiddenField(default=serializers.CurrentUserDefault()) + owner = serializers.HiddenField(default=serializers.CurrentUserDefault()) class Meta: model = Pets - exclude = () + exclude = () diff --git a/furbaby/api/urls.py b/furbaby/api/urls.py index c98af16..e684b6b 100644 --- a/furbaby/api/urls.py +++ b/furbaby/api/urls.py @@ -2,7 +2,7 @@ from . import views from .views import UserRegistrationView -from .views import UserLoginView,PetListCreateView,PetRetrieveUpdateDeleteView +from .views import UserLoginView, PetListCreateView, PetRetrieveUpdateDeleteView # csrf @@ -22,6 +22,8 @@ include("django_rest_passwordreset.urls", namespace="password_reset"), ), path("api/user", views.user_view, name="user-info"), - path('pets/', PetListCreateView.as_view(), name='pet-list-create'), - path('pets//', PetRetrieveUpdateDeleteView.as_view(), name='pet-retrieve-update-delete'), + path("pets/", PetListCreateView.as_view(), name="pet-list-create"), + path( + "pets//", PetRetrieveUpdateDeleteView.as_view(), name="pet-retrieve-update-delete" + ), ] diff --git a/furbaby/api/views.py b/furbaby/api/views.py index da15cf7..1b11686 100644 --- a/furbaby/api/views.py +++ b/furbaby/api/views.py @@ -6,7 +6,7 @@ from rest_framework.views import APIView from rest_framework.generics import GenericAPIView from django.views.decorators.csrf import ensure_csrf_cookie -from rest_framework.generics import ListCreateAPIView,RetrieveUpdateDestroyAPIView +from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from .utils import json_response @@ -67,6 +67,7 @@ def post(self, request): ) return json_response(data=serializer.errors, status=status.HTTP_401_UNAUTHORIZED) + @api_view(["GET", "OPTIONS", "POST"]) def logout_view(request): if not request.user.is_authenticated: @@ -180,6 +181,7 @@ def password_reset_token_created(sender, instance, reset_password_token, *args, msg.attach_alternative(email_html_message, "text/html") msg.send() + class PetListCreateView(ListCreateAPIView): queryset = Pets.objects.all() serializer_class = PetSerializer @@ -190,7 +192,8 @@ def create(self, request, *args, **kwargs): request.data["owner_id"] = request.user.id return super().create(request, *args, **kwargs) + class PetRetrieveUpdateDeleteView(RetrieveUpdateDestroyAPIView): queryset = Pets.objects.all() serializer_class = PetSerializer - permission_classes = [IsAuthenticated] \ No newline at end of file + permission_classes = [IsAuthenticated] From cf0783397228ea7ca421809966d1252343a6c630 Mon Sep 17 00:00:00 2001 From: Yami Naik Date: Tue, 7 Nov 2023 11:51:56 -0500 Subject: [PATCH 10/16] Permission to allow owner to create pets --- furbaby/api/views.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/furbaby/api/views.py b/furbaby/api/views.py index 1b11686..ac40314 100644 --- a/furbaby/api/views.py +++ b/furbaby/api/views.py @@ -21,6 +21,7 @@ from django_rest_passwordreset.signals import reset_password_token_created +from rest_framework.exceptions import PermissionDenied class UserRegistrationView(GenericAPIView): # the next line is to disable CORS for that endpoint/view @@ -187,11 +188,18 @@ class PetListCreateView(ListCreateAPIView): serializer_class = PetSerializer permission_classes = [IsAuthenticated] + def get_queryset(self): + # You can remove this method from PetRetrieveUpdateDeleteView + return Pets.objects.filter(owner_id=self.request.user.id) + def create(self, request, *args, **kwargs): - # Set the owner to the authenticated user request.data["owner_id"] = request.user.id - return super().create(request, *args, **kwargs) - + print(request.user.user_type) + if "owner" in request.user.user_type: + request.data["owner_id"] = request.user.id + return super().create(request, *args, **kwargs) + else: + raise PermissionDenied("You are not allowed to create a pet profile.") class PetRetrieveUpdateDeleteView(RetrieveUpdateDestroyAPIView): queryset = Pets.objects.all() From 3d7143e50e5d145c650c4ddaf4fa3bc265a80ff8 Mon Sep 17 00:00:00 2001 From: Yami Naik Date: Tue, 7 Nov 2023 11:54:54 -0500 Subject: [PATCH 11/16] travis error solved --- furbaby/api/views.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/furbaby/api/views.py b/furbaby/api/views.py index ac40314..fc3a42b 100644 --- a/furbaby/api/views.py +++ b/furbaby/api/views.py @@ -23,6 +23,7 @@ from rest_framework.exceptions import PermissionDenied + class UserRegistrationView(GenericAPIView): # the next line is to disable CORS for that endpoint/view authentication_classes = [] @@ -201,6 +202,7 @@ def create(self, request, *args, **kwargs): else: raise PermissionDenied("You are not allowed to create a pet profile.") + class PetRetrieveUpdateDeleteView(RetrieveUpdateDestroyAPIView): queryset = Pets.objects.all() serializer_class = PetSerializer From 51f39bad41352f877513d52100c0742bb84070ac Mon Sep 17 00:00:00 2001 From: Yami Naik Date: Sun, 12 Nov 2023 13:28:41 -0500 Subject: [PATCH 12/16] JOB API --- furbaby/api/serializers.py | 8 ++++- furbaby/api/urls.py | 3 +- furbaby/api/views.py | 63 ++++++++++++++++++++++++++++++++++---- 3 files changed, 66 insertions(+), 8 deletions(-) diff --git a/furbaby/api/serializers.py b/furbaby/api/serializers.py index 759ef9a..3aac91d 100644 --- a/furbaby/api/serializers.py +++ b/furbaby/api/serializers.py @@ -1,5 +1,5 @@ from rest_framework import serializers -from .models import Users, Pets +from .models import Users, Pets, Jobs from django.contrib.auth.hashers import make_password from django.core.exceptions import ValidationError @@ -53,3 +53,9 @@ class PetSerializer(serializers.ModelSerializer): class Meta: model = Pets exclude = () + +class JobSerializer(serializers.ModelSerializer): + user = serializers.HiddenField(default=serializers.CurrentUserDefault()) + class Meta: + model = Jobs + fields = '__all__' \ No newline at end of file diff --git a/furbaby/api/urls.py b/furbaby/api/urls.py index e684b6b..70d04ff 100644 --- a/furbaby/api/urls.py +++ b/furbaby/api/urls.py @@ -2,7 +2,7 @@ from . import views from .views import UserRegistrationView -from .views import UserLoginView, PetListCreateView, PetRetrieveUpdateDeleteView +from .views import UserLoginView, PetListCreateView, PetRetrieveUpdateDeleteView,JobView # csrf @@ -26,4 +26,5 @@ path( "pets//", PetRetrieveUpdateDeleteView.as_view(), name="pet-retrieve-update-delete" ), + path("jobs/", JobView.as_view(), name="custom-job-view") ] diff --git a/furbaby/api/views.py b/furbaby/api/views.py index fc3a42b..ada8d0c 100644 --- a/furbaby/api/views.py +++ b/furbaby/api/views.py @@ -13,16 +13,14 @@ from django.conf import settings from rest_framework.decorators import api_view from api.auth_backends import EmailBackend -from .serializers import RegistrationSerializer, UserLoginSerializer, PetSerializer -from .models import Pets +from .serializers import RegistrationSerializer, UserLoginSerializer, PetSerializer, JobSerializer +from .models import Pets, Jobs from django.core.mail import EmailMultiAlternatives from django.dispatch import receiver from django.template.loader import render_to_string - from django_rest_passwordreset.signals import reset_password_token_created - from rest_framework.exceptions import PermissionDenied - +from django.core.exceptions import ValidationError class UserRegistrationView(GenericAPIView): # the next line is to disable CORS for that endpoint/view @@ -190,7 +188,6 @@ class PetListCreateView(ListCreateAPIView): permission_classes = [IsAuthenticated] def get_queryset(self): - # You can remove this method from PetRetrieveUpdateDeleteView return Pets.objects.filter(owner_id=self.request.user.id) def create(self, request, *args, **kwargs): @@ -207,3 +204,57 @@ class PetRetrieveUpdateDeleteView(RetrieveUpdateDestroyAPIView): queryset = Pets.objects.all() serializer_class = PetSerializer permission_classes = [IsAuthenticated] + +class JobView(APIView): + permission_classes = [IsAuthenticated] + + def get_queryset(self): + return Jobs.objects.filter(user_id=self.request.user.id) + + def get_object(self, job_id): + try: + return Jobs.objects.get(id=job_id, user=self.request.user) + except Jobs.DoesNotExist: + raise ValidationError("Job not found or you do not have permission to access this job.") + + def get(self, request, *args, **kwargs): + job_id = self.request.data.get('id') + if job_id: + job = self.get_object(job_id) + serializer = JobSerializer(job) + return JsonResponse(serializer.data) + else: + queryset = self.get_queryset() + serializer = JobSerializer(queryset, many=True) + return JsonResponse(serializer.data, safe=False) + + def post(self, request, *args, **kwargs): + request.data["user_id"] = request.user.id + print(request.user.user_type) + if "owner" in request.user.user_type: + pet_id = self.request.data.get('pet') + try: + pet = Pets.objects.get(id=pet_id, owner=self.request.user) + except Pets.DoesNotExist: + raise ValidationError("Invalid pet ID or you do not own the pet.") + + # Ensure that the pet owner is the one creating the job + if pet.owner != self.request.user: + raise PermissionDenied("You do not have permission to create a job for this pet.") + + # Now, create the job with the specified pet + serializer = JobSerializer(data=request.data, context={'request': request}) + serializer.is_valid(raise_exception=True) + serializer.save(user=self.request.user, pet=pet) + + return Response(serializer.data, status=status.HTTP_201_CREATED) + else: + raise PermissionDenied("You are not allowed to create a job.") + + + def delete(self, request, *args, **kwargs): + job_id = self.request.data.get('id') + if job_id: + job = self.get_object(job_id) + job.delete() + return JsonResponse({"detail": "Job deleted successfully."}) \ No newline at end of file From fd6f05292e74e36845ec7bc23ee518140d37ee3e Mon Sep 17 00:00:00 2001 From: Yami Naik Date: Sun, 12 Nov 2023 13:36:07 -0500 Subject: [PATCH 13/16] travis error solved --- furbaby/api/serializers.py | 4 +++- furbaby/api/urls.py | 4 ++-- furbaby/api/views.py | 13 +++++++------ 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/furbaby/api/serializers.py b/furbaby/api/serializers.py index 3aac91d..98f04d8 100644 --- a/furbaby/api/serializers.py +++ b/furbaby/api/serializers.py @@ -54,8 +54,10 @@ class Meta: model = Pets exclude = () + class JobSerializer(serializers.ModelSerializer): user = serializers.HiddenField(default=serializers.CurrentUserDefault()) + class Meta: model = Jobs - fields = '__all__' \ No newline at end of file + fields = "__all__" diff --git a/furbaby/api/urls.py b/furbaby/api/urls.py index 70d04ff..0d95352 100644 --- a/furbaby/api/urls.py +++ b/furbaby/api/urls.py @@ -2,7 +2,7 @@ from . import views from .views import UserRegistrationView -from .views import UserLoginView, PetListCreateView, PetRetrieveUpdateDeleteView,JobView +from .views import UserLoginView, PetListCreateView, PetRetrieveUpdateDeleteView, JobView # csrf @@ -26,5 +26,5 @@ path( "pets//", PetRetrieveUpdateDeleteView.as_view(), name="pet-retrieve-update-delete" ), - path("jobs/", JobView.as_view(), name="custom-job-view") + path("jobs/", JobView.as_view(), name="custom-job-view"), ] diff --git a/furbaby/api/views.py b/furbaby/api/views.py index ada8d0c..78ef629 100644 --- a/furbaby/api/views.py +++ b/furbaby/api/views.py @@ -22,6 +22,7 @@ from rest_framework.exceptions import PermissionDenied from django.core.exceptions import ValidationError + class UserRegistrationView(GenericAPIView): # the next line is to disable CORS for that endpoint/view authentication_classes = [] @@ -205,6 +206,7 @@ class PetRetrieveUpdateDeleteView(RetrieveUpdateDestroyAPIView): serializer_class = PetSerializer permission_classes = [IsAuthenticated] + class JobView(APIView): permission_classes = [IsAuthenticated] @@ -218,7 +220,7 @@ def get_object(self, job_id): raise ValidationError("Job not found or you do not have permission to access this job.") def get(self, request, *args, **kwargs): - job_id = self.request.data.get('id') + job_id = self.request.data.get("id") if job_id: job = self.get_object(job_id) serializer = JobSerializer(job) @@ -232,7 +234,7 @@ def post(self, request, *args, **kwargs): request.data["user_id"] = request.user.id print(request.user.user_type) if "owner" in request.user.user_type: - pet_id = self.request.data.get('pet') + pet_id = self.request.data.get("pet") try: pet = Pets.objects.get(id=pet_id, owner=self.request.user) except Pets.DoesNotExist: @@ -243,7 +245,7 @@ def post(self, request, *args, **kwargs): raise PermissionDenied("You do not have permission to create a job for this pet.") # Now, create the job with the specified pet - serializer = JobSerializer(data=request.data, context={'request': request}) + serializer = JobSerializer(data=request.data, context={"request": request}) serializer.is_valid(raise_exception=True) serializer.save(user=self.request.user, pet=pet) @@ -251,10 +253,9 @@ def post(self, request, *args, **kwargs): else: raise PermissionDenied("You are not allowed to create a job.") - def delete(self, request, *args, **kwargs): - job_id = self.request.data.get('id') + job_id = self.request.data.get("id") if job_id: job = self.get_object(job_id) job.delete() - return JsonResponse({"detail": "Job deleted successfully."}) \ No newline at end of file + return JsonResponse({"detail": "Job deleted successfully."}) From bd89f0f54d7955ef0790de86dff8168d8015dab7 Mon Sep 17 00:00:00 2001 From: Yami Naik Date: Sun, 19 Nov 2023 17:40:45 -0500 Subject: [PATCH 14/16] Pet Profile UI --- frontend/src/App.tsx | 8 + frontend/src/Home.tsx | 4 +- frontend/src/PetProfiles.tsx | 355 +++++++++++++++++++++++++++++++++++ frontend/src/constants.tsx | 2 + 4 files changed, 368 insertions(+), 1 deletion(-) create mode 100644 frontend/src/PetProfiles.tsx diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 1738538..7a1c84d 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -9,6 +9,8 @@ import Landing from "./Landing"; import Loading from "./Loading"; import NotFound from "./NotFound"; import SignUp from "./SignUp"; +import PetProfiles from "./PetProfiles"; + const ProtectedRoute = ({ children }: React.PropsWithChildren) => { const { authenticationState } = useContext(AuthContext); @@ -85,6 +87,12 @@ const AppRouter = () => { } /> + + } + /> } /> ); diff --git a/frontend/src/Home.tsx b/frontend/src/Home.tsx index 4bb40c0..6dcbabc 100644 --- a/frontend/src/Home.tsx +++ b/frontend/src/Home.tsx @@ -7,6 +7,7 @@ import { AuthCtx } from "./auth/AuthProvider"; import { ROUTES } from "./constants"; import FurBabyLogo from "./FurbabyLogo"; import Profile from "./Profile"; +import PetProfiles from './PetProfiles'; import Settings from "./Settings"; import { classNames } from "./utils"; @@ -19,7 +20,7 @@ const user = { const navigation = [ { name: "Dashboard", href: "#", current: true }, { name: "Job Feed", href: "#", current: false }, - { name: "Pet Profiles", href: "#", current: false }, + { name: "Pet Profiles", href: ROUTES.PROTECTED_ROUTES.PET_PROFILES, current: true }, ]; type HomeProps = { @@ -83,6 +84,7 @@ const Home = (props: React.PropsWithChildren) => { /> ); } + return "Nothing here to display"; }, [pathname]); diff --git a/frontend/src/PetProfiles.tsx b/frontend/src/PetProfiles.tsx new file mode 100644 index 0000000..1967935 --- /dev/null +++ b/frontend/src/PetProfiles.tsx @@ -0,0 +1,355 @@ +import React, { useState, useEffect } from 'react'; +import axios from 'axios'; +import { Tab } from "@headlessui/react"; +import { API_ROUTES } from "./constants"; +import toast from "react-hot-toast"; + + +interface Pet { + id: string; + name: string; + species: string; + color: string; + height: string; + breed: string; + weight: string; + pictures: string[]; + chip_number: string; + health_requirements: string; +} + +interface EditPetFormData { + name: string; + species: string; + breed: string; + weight: string; + pictures: string[]; +} + +interface PetProfilePageProps { } + +const PetProfiles: React.FC = () => { + const [pets, setPets] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [editingPet, setEditingPet] = useState(null); + const [editFormData, setEditFormData] = useState({ + name: '', + species: '', + breed: '', + weight: '', + }); + + useEffect(() => { + fetchPets(); + }, []); + + const fetchPets = async () => { + try { + const response = await axios.get(`${API_ROUTES.PETS}`); + + if (response.status !== 200) { + throw new Error(`Failed to fetch pets. Status: ${response.status}`); + } + + setPets(response.data); + } catch (error: any) { + console.error('Error fetching pets:', error.message); + setError('Failed to fetch pets. Please try again.'); + } finally { + setLoading(false); + } + }; + + const handleDelete = async (petId: string) => { + const deleteConsent = window.confirm("Are you sure you want to delete this pet?"); + if (deleteConsent) { + try { + const response = await axios.delete(`${API_ROUTES.PETS}${petId}`); + + if (response.status === 204) { + setPets((prevPets) => prevPets.filter((pet) => pet.id !== petId)); + toast.success("Pet profile deleted successfully"); + } else { + throw new Error('Failed to delete pet profile'); + } + } catch (err) { + console.error(err); + toast.error("Failed to delete pet profile"); + } + } + }; + + const handleEdit = (pet: Pet) => { + setEditingPet(pet); + setEditFormData({ + name: pet.name, + species: pet.species, + breed: pet.breed, + weight: pet.weight, + }); + }; + + const handleEditCancel = () => { + setEditingPet(null); + setEditFormData({ + name: '', + species: '', + breed: '', + weight: '', + }); + }; + + const handleEditSave = async (petId: string) => { + const saveConsent = window.confirm("Are you sure you want to save these changes?"); + if (saveConsent) { + try { + const response = await axios.put(`${API_ROUTES.PETS}${petId}/`, { + name: editFormData.name, + species: editFormData.species, + breed: editFormData.breed, + weight: editFormData.weight, + }); + + if (response.status === 200) { + const updatedPetIndex = pets.findIndex((pet) => pet.id === petId); + if (updatedPetIndex !== -1) { + const updatedPets = [...pets]; + updatedPets[updatedPetIndex] = { + ...updatedPets[updatedPetIndex], + name: editFormData.name, + species: editFormData.species, + breed: editFormData.breed, + weight: editFormData.weight, + }; + setPets(updatedPets); + } + + toast.success("Pet profile updated successfully"); + setEditingPet(null); + } else { + throw new Error('Failed to edit pet profile'); + } + } catch (err) { + console.error(err); + toast.error("Failed to edit pet profile"); + } + } + }; + + if (loading) { + return

Loading...

; + } + + return ( +
+

Pet Profiles

+ {error &&

{error}

} +
    + {pets.map((pet: Pet) => ( +
  • +
    + {editingPet === pet ? ( +
    +

    Edit Pet Profile

    +
    + {/* Include your form fields here */} + + setEditFormData({ ...editFormData, name: e.target.value })} + className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm" + /> + {/* ... Repeat for other fields */} +
    +
    + + +
    +
    + ) : ( +
    +

    Name: {pet.name}

    +

    Species: {pet.species}

    +

    Color: {pet.color}

    + {/* ... Display other pet information */} +
    + )} +
    + {!editingPet && ( + + )} + +
    +
    +
  • + ))} +
+
+ ); +}; + + +const PetProfilePage: React.FC = () => { + const [activeTab, setActiveTab] = useState('view'); + const [petFormData, setPetFormData] = useState({ + name: '', + species: '', + breed: '', + weight: '', + pictures: ['url1', 'url2', 'url3'], + }); + + const onClickSave = () => { + const saveConsent = window.confirm("Are you sure you want to make these changes?"); + if (saveConsent) { + axios.post(API_ROUTES.PETS, petFormData) + .then((response) => { + if (response.status === 201) { + toast.success("Pet profile updated successfully"); + setPetFormData({ + name: '', + species: '', + breed: '', + weight: '', + pictures: ['url1', 'url2', 'url3'], + }); + } else { + throw new Error('Failed to save pet profile'); + } + }) + .catch((err) => { + console.error(err); + toast.error("Failed to update pet profile"); + }); + } + }; + + const onClickCancel = () => { + const cancelConsent = window.confirm("Are you sure you want to discard these changes?"); + if (cancelConsent) { + setPetFormData({ + name: '', + species: '', + breed: '', + weight: '', + pictures: ['url1', 'url2', 'url3'], + }); + } + }; + + return ( +
+ + + selected ? "bg-white text-blue-500" : "bg-gray-200 text-gray-600"} + onClick={() => setActiveTab('view')} + > + View Pet Profiles + + selected ? "bg-white text-blue-500" : "bg-gray-200 text-gray-600"} + onClick={() => setActiveTab('add')} + > + Add Pet Profile + + + + + {activeTab === 'view' && } + + + {activeTab === 'add' && ( +
+ + setPetFormData({ ...petFormData, name: e.target.value })} + className="border border-gray-300 rounded-md p-2 mt-1" + /> + + setPetFormData({ ...petFormData, species: e.target.value })} + className="border border-gray-300 rounded-md p-2 mt-1" + /> + + setPetFormData({ ...petFormData, breed: e.target.value })} + className="border border-gray-300 rounded-md p-2 mt-1" + /> + + setPetFormData({ ...petFormData, weight: e.target.value })} + className="border border-gray-300 rounded-md p-2 mt-1" + /> +
+ )} +
+ + +
+
+
+
+
+ ); +}; + +export default PetProfilePage; diff --git a/frontend/src/constants.tsx b/frontend/src/constants.tsx index 469da36..e0721c5 100644 --- a/frontend/src/constants.tsx +++ b/frontend/src/constants.tsx @@ -28,6 +28,7 @@ export const ROUTES = { HOME: "/home", PROFILE: "/profile", SETTINGS: "/settings", + PET_PROFILES: "/pet-profiles", }, } as const; @@ -48,4 +49,5 @@ export const API_ROUTES = { USER: { USER_ROOT: "api/user", }, + PETS: "pets/" } as const; From ee33dc59dfda626afa076593c7804dbfadaab332 Mon Sep 17 00:00:00 2001 From: Yami Naik Date: Sun, 19 Nov 2023 18:25:14 -0500 Subject: [PATCH 15/16] Resolve merge conflicts --- furbaby/api/serializers.py | 2 +- furbaby/furbaby/local_settings.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/furbaby/api/serializers.py b/furbaby/api/serializers.py index 1f1b1a1..f082092 100644 --- a/furbaby/api/serializers.py +++ b/furbaby/api/serializers.py @@ -1,5 +1,5 @@ from rest_framework import serializers -from .models import Users, Locations, Pets +from .models import Users, Locations, Pets, Jobs from django.contrib.auth.hashers import make_password from django.core.exceptions import ValidationError diff --git a/furbaby/furbaby/local_settings.py b/furbaby/furbaby/local_settings.py index 037618f..4c88d5c 100644 --- a/furbaby/furbaby/local_settings.py +++ b/furbaby/furbaby/local_settings.py @@ -19,7 +19,7 @@ os.environ.setdefault("FORGOT_PASSWORD_HOST", "http://localhost:3000") -load_dotenv("/Users/yaminaik/INET-Monday-Fall2023-Team-1/furbaby/furbaby/.dotenv") +load_dotenv() # Build paths inside the project like this: BASE_DIR / 'subdir'. @@ -92,9 +92,9 @@ DATABASES = { "default": { "ENGINE": "django.db.backends.postgresql", - "NAME": "demo1", - "USER": "yami", - "PASSWORD": "yami", + "NAME": "postgres", + "USER": "postgres", + "PASSWORD": "postgres", "HOST": "localhost", "PORT": "5432", "ATOMIC_REQUESTS": True, From f2778b7b55cc213767ecac5a83264da097c2983d Mon Sep 17 00:00:00 2001 From: Yami Naik Date: Sun, 19 Nov 2023 19:20:48 -0500 Subject: [PATCH 16/16] Pet Profile UI changes --- frontend/src/PetProfiles.tsx | 97 ++++++++++++++++++++++++++++++++++-- 1 file changed, 94 insertions(+), 3 deletions(-) diff --git a/frontend/src/PetProfiles.tsx b/frontend/src/PetProfiles.tsx index 1967935..0dff99a 100644 --- a/frontend/src/PetProfiles.tsx +++ b/frontend/src/PetProfiles.tsx @@ -153,7 +153,6 @@ const PetProfiles: React.FC = () => {

Edit Pet Profile

- {/* Include your form fields here */} { onChange={(e) => setEditFormData({ ...editFormData, name: e.target.value })} className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm" /> - {/* ... Repeat for other fields */} + + setEditFormData({ ...editFormData, species: e.target.value })} + className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm" + /> + + setEditFormData({ ...editFormData, breed: e.target.value })} + className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm" + /> + + setEditFormData({ ...editFormData, weight: e.target.value })} + className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm" + /> +