From 24bf10ecff5ebad9eb6c7428c9ac9f5e48122cfd Mon Sep 17 00:00:00 2001 From: Enrker Date: Sun, 14 Apr 2024 14:54:39 -0400 Subject: [PATCH 01/33] =?UTF-8?q?resum=C3=AD=20la=20descripci=C3=B3n=20de?= =?UTF-8?q?=20un=20parrafo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pet/templates/pet/our_pets_section.html | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pet/templates/pet/our_pets_section.html b/pet/templates/pet/our_pets_section.html index 52ddc96..2392a92 100644 --- a/pet/templates/pet/our_pets_section.html +++ b/pet/templates/pet/our_pets_section.html @@ -4,10 +4,7 @@

Conoce a nuestras adorables mascotas

- Cada día es una oportunidad para conocer a nuestras adorables mascotas. Cada una de ellas tiene una historia - única y está esperando a alguien especial como tú. Nuestras mascotas son cariñosas, juguetonas y están listas - para ser parte de tu familia. ¡Ven y descubre el amor incondicional que una mascota puede ofrecer! Recuerda, - adoptar una mascota no solo cambia la vida de la mascota, sino también la tuya. + Cada día es una oportunidad para conocer a nuestras adorables mascotas. Son cariñosas, juguetonas y están listas para ser parte de tu familia. Ven y descubre el amor incondicional que una mascota puede ofrecer. Recuerda, adoptar una mascota no solo cambia la vida de la mascota, sino también la tuya. 🐾❤️

Descubre más sobre nuestras mascotas From bda93e8eb5182671c19b785efb4553a8ee931dce Mon Sep 17 00:00:00 2001 From: Fernando Date: Mon, 15 Apr 2024 01:04:22 -0400 Subject: [PATCH 02/33] Update django.yml --- .github/workflows/django.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/django.yml b/.github/workflows/django.yml index afb15c9..09d7b16 100644 --- a/.github/workflows/django.yml +++ b/.github/workflows/django.yml @@ -27,8 +27,8 @@ jobs: - name: Set Environment Variables env: SECRET_KEY: ${{ secrets.SECRET_KEY }} - run: echo "SECRET_KEY=${SECRET_KEY}" > /home/runner/work/Albercan-Backend/Albercan-Backend/envs/.env + run: echo "SECRET_KEY=${SECRET_KEY}" > ./envs/.env - name: Install Dependencies run: poetry install - name: Run Tests - run: poetry run python manage.py test \ No newline at end of file + run: poetry run python manage.py test From b760da6478585ecd43cb032bd3041d972ba7e64f Mon Sep 17 00:00:00 2001 From: Fer-Bar Date: Thu, 23 May 2024 18:07:20 -0400 Subject: [PATCH 03/33] Revert carousel.js where the movement is fluid --- pet/static/pet/js/carousel.js | 100 +++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 43 deletions(-) diff --git a/pet/static/pet/js/carousel.js b/pet/static/pet/js/carousel.js index bace4d9..00aed83 100644 --- a/pet/static/pet/js/carousel.js +++ b/pet/static/pet/js/carousel.js @@ -1,59 +1,47 @@ +const wrapper = document.querySelector(".wrapper-carousel"); const carousel = document.querySelector(".carousel-secondary"); +const firstCardWidth = carousel.querySelector(".card-secondary").offsetWidth; const arrowBtns = document.querySelectorAll(".wrapper-carousel i"); -let carouselChildren = [...carousel.children]; -const cardCount = carouselChildren.length; +const carouselChildren = [...carousel.children]; -let isDragging = false, startX, startScrollLeft; +let isDragging = false, isAutoPlay = false, startX, startScrollLeft, timeoutId; -function getFirstCardWidth() { - return carousel.querySelector("ul > li.card-secondary").offsetWidth; // 1st secondary-card -} +// Get the number of cards that can fit in the carousel at once +let cardPerView = Math.round(carousel.offsetWidth / firstCardWidth); -function getCardPerView() { - return Math.round(carousel.offsetWidth / getFirstCardWidth()); -} +// Insert copies of the last few cards to beginning of carousel for infinite scrolling +carouselChildren.slice(-cardPerView).reverse().forEach(card => { + carousel.insertAdjacentHTML("afterbegin", card.outerHTML); +}); -// Add event listeners for the arrow buttons to scroll the carousel left and right -function addArrowButtonListeners() { - arrowBtns.forEach(btn => { - btn.addEventListener("click", () => { - let firstCardWidth = getFirstCardWidth(); - carousel.scrollLeft += btn.id === "left" ? -firstCardWidth : firstCardWidth; +// Insert copies of the first few cards to end of carousel for infinite scrolling +carouselChildren.slice(0, cardPerView).forEach(card => { + carousel.insertAdjacentHTML("beforeend", card.outerHTML); +}); - if (btn.id === "left") { - // Remove the last card and add it to the beginning - let lastCard = carouselChildren.pop(); - carouselChildren.unshift(lastCard); - carousel.insertBefore(lastCard, carousel.firstChild); - } else { - // Remove the first card and add it to the end - let firstCard = carouselChildren.shift(); - carouselChildren.push(firstCard); - carousel.removeChild(firstCard); - carousel.appendChild(firstCard); - } - }); - }); -} +// Scroll the carousel at appropriate position to hide first few duplicate cards on Firefox +carousel.classList.add("no-transition"); +carousel.scrollLeft = carousel.offsetWidth; +carousel.classList.remove("no-transition"); -function checkArrowVisibility() { - let cardPerView = getCardPerView(); - if (cardCount >= cardPerView) { - arrowBtns.forEach(btn => btn.style.display = "block"); - } else { - arrowBtns.forEach(btn => btn.style.display = "none"); - } -} +// Add event listeners for the arrow buttons to scroll the carousel left and right +arrowBtns.forEach(btn => { + btn.addEventListener("click", () => { + carousel.scrollLeft += btn.id === "left" ? -firstCardWidth : firstCardWidth; + }); +}); const dragStart = (e) => { isDragging = true; carousel.classList.add("dragging"); + // Records the initial cursor and scroll position of the carousel startX = e.pageX; startScrollLeft = carousel.scrollLeft; } const dragging = (e) => { - if(!isDragging) return; + if(!isDragging) return; // if isDragging is false return from here + // Updates the scroll position of the carousel based on the cursor movement carousel.scrollLeft = startScrollLeft - (e.pageX - startX); } @@ -62,9 +50,35 @@ const dragStop = () => { carousel.classList.remove("dragging"); } -addArrowButtonListeners(); -window.addEventListener("load", checkArrowVisibility); -window.addEventListener("resize", checkArrowVisibility); +const infiniteScroll = () => { + // If the carousel is at the beginning, scroll to the end + if(carousel.scrollLeft === 0) { + carousel.classList.add("no-transition"); + carousel.scrollLeft = carousel.scrollWidth - (2 * carousel.offsetWidth); + carousel.classList.remove("no-transition"); + } + // If the carousel is at the end, scroll to the beginning + else if(Math.ceil(carousel.scrollLeft) === carousel.scrollWidth - carousel.offsetWidth) { + carousel.classList.add("no-transition"); + carousel.scrollLeft = carousel.offsetWidth; + carousel.classList.remove("no-transition"); + } + + // Clear existing timeout & start autoplay if mouse is not hovering over carousel + clearTimeout(timeoutId); + if(!wrapper.matches(":hover")) autoPlay(); +} + +const autoPlay = () => { + if(window.innerWidth < 800 || !isAutoPlay) return; // Return if window is smaller than 800 or isAutoPlay is false + // Autoplay the carousel after every 2500 ms + timeoutId = setTimeout(() => carousel.scrollLeft += firstCardWidth, 2500); +} +autoPlay(); + carousel.addEventListener("mousedown", dragStart); carousel.addEventListener("mousemove", dragging); -document.addEventListener("mouseup", dragStop); \ No newline at end of file +document.addEventListener("mouseup", dragStop); +carousel.addEventListener("scroll", infiniteScroll); +wrapper.addEventListener("mouseenter", () => clearTimeout(timeoutId)); +wrapper.addEventListener("mouseleave", autoPlay); \ No newline at end of file From 0c6a66f2b4d529159f1cd90d1ed5977a170df633 Mon Sep 17 00:00:00 2001 From: Fer-Bar Date: Thu, 23 May 2024 18:09:10 -0400 Subject: [PATCH 04/33] Show secondary_carousel only if there pets objects --- pet/templates/pet/secondary_carousel.html | 34 ++++++++++++----------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/pet/templates/pet/secondary_carousel.html b/pet/templates/pet/secondary_carousel.html index 660dd76..13b5dc8 100644 --- a/pet/templates/pet/secondary_carousel.html +++ b/pet/templates/pet/secondary_carousel.html @@ -1,17 +1,19 @@

- -
\ No newline at end of file + {% if pets %} + + {% endif %} +
From f3cc2d16796b95575c6972250c171a501eb9fa05 Mon Sep 17 00:00:00 2001 From: Fer-Bar Date: Thu, 23 May 2024 19:40:10 -0400 Subject: [PATCH 05/33] Added picture_url property to return safe url strings --- people/models.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/people/models.py b/people/models.py index 4534dfe..886eca9 100644 --- a/people/models.py +++ b/people/models.py @@ -145,6 +145,15 @@ class Person(models.Model): help_text=_("Max size allowed: 20Mbs"), ) + @property + def picture_url(self): + """ + Return self.picture.url if self.picture is not None, + 'url' exist and has a value, else, return None. + """ + if self.picture and hasattr(self.picture, "url"): + return self.picture.url + def create_user(self): if self.user_id is not None: return From 6ea8408f26940927a45b37f061c1970a46ce1939 Mon Sep 17 00:00:00 2001 From: Fer-Bar Date: Thu, 23 May 2024 19:42:39 -0400 Subject: [PATCH 06/33] Deleted user object references, refactor the orden fields & added picture_url property --- people/admin.py | 37 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/people/admin.py b/people/admin.py index f63ed2f..8c4d687 100644 --- a/people/admin.py +++ b/people/admin.py @@ -25,34 +25,30 @@ class BasePersonAdmin(ModelAdmin): "user__email", ] autocomplete_fields = [ - "user", "occupation", "city", "nationality", ] def picture_display(self, obj=None): - return mark_safe(f'') + return mark_safe( + f'' + ) - picture_display.short_description = _("See picture") + picture_display.short_description = _("Profile Picture") - def get_readonly_fields(self, request, obj=None): - return ["picture_display",] + readonly_fields = ("picture_display",) def get_fieldsets(self, request, obj=None): if obj is None or not obj.picture: main_fields = [ ("picture",), - ("first_name", "last_name", "gender"), ] else: main_fields = [ ( "picture_display", "picture", - "first_name", - "last_name", - "gender", ), ] @@ -62,7 +58,7 @@ def get_fieldsets(self, request, obj=None): { "fields": main_fields + [ - ("city",), + ("first_name", "last_name", "gender", "city"), ("personal_email", "birthday"), ] }, @@ -74,10 +70,10 @@ def get_fieldsets(self, request, obj=None): ("document_type", "document_id"), ("marital_status",), ("phone_number", "secondary_phone_number"), - ("nationality", ), - ("address", ), - ("neighborhood", ), - ("occupation", ), + ("nationality",), + ("address",), + ("neighborhood",), + ("occupation",), ] }, ), @@ -86,20 +82,17 @@ def get_fieldsets(self, request, obj=None): @admin.register(Person) class PersonAdmin(BasePersonAdmin): - - def get_fieldsets(self, request, obj=None): - fieldsets = super().get_fieldsets(request, obj=obj) - return fieldsets + list_display = ["first_name", "last_name", "list_pets",] + inlines = [ + PetInline, + ] def has_delete_permission( self, request, obj=None ): # This allows to delete only if you're a superuser return request.user.is_superuser and request.user.is_active - inlines = [PetInline,] - list_display = ["first_name", "last_name", "list_pets"] - def list_pets(self, obj): return ", ".join([str(pet) for pet in obj.pets.all()]) - list_pets.short_description = _("Pets") + list_pets.short_description = _("My Pets") From c0f36f307e724a9a7a0e3b5b3e495cc058c3c020 Mon Sep 17 00:00:00 2001 From: Fer-Bar Date: Thu, 23 May 2024 19:54:53 -0400 Subject: [PATCH 07/33] Added picture profile in admin section, declaring the field as a readonly --- pet/admin.py | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/pet/admin.py b/pet/admin.py index 0f87689..7e9b6d9 100644 --- a/pet/admin.py +++ b/pet/admin.py @@ -1,4 +1,6 @@ from django.contrib import admin +from django.utils.safestring import mark_safe +from django.utils.translation import gettext_lazy as _ from pet.filters import HasOwnerFilter from pet.models import Breed, Pet @@ -8,9 +10,33 @@ class PetAdmin(admin.ModelAdmin): search_fields = ["name", "owner"] list_display = ("name", "breed", "gender", "age", "owner", "has_owner", "is_neutered") - readonly_fields = ("age", ) + readonly_fields = ("age", "picture_display",) list_filter = (HasOwnerFilter,) + def picture_display(self, obj=None): + return mark_safe( + f'' + ) + + picture_display.short_description = _("Profile Picture") + + def get_fieldsets(self, request, obj=None): + if obj is None or not obj.picture_url: + picture_fields = [ + ("picture",), + ] + else: + picture_fields = [ + ("picture_display", "picture"), + ] + return ( + ( + None, { + "fields": picture_fields + [("name", "breed", "gender", "age"), ] + } + ), + ) + @admin.register(Breed) class BreedAdmin(admin.ModelAdmin): @@ -19,4 +45,5 @@ class BreedAdmin(admin.ModelAdmin): class PetInline(admin.TabularInline): model = Pet + fields = ("name", "breed", "gender") extra = 0 From 469e0bea74cc16511c848fa5be1bbcb87b68de39 Mon Sep 17 00:00:00 2001 From: Fer-Bar Date: Sat, 25 May 2024 21:28:45 -0400 Subject: [PATCH 08/33] Added signals to people app to create a user instance after person instance creation --- people/apps.py | 5 +++++ people/signals.py | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 people/signals.py diff --git a/people/apps.py b/people/apps.py index 91fcc9c..248f327 100644 --- a/people/apps.py +++ b/people/apps.py @@ -1,6 +1,11 @@ from django.apps import AppConfig +from django.utils.translation import gettext_lazy as _ class PeopleConfig(AppConfig): default_auto_field = "django.db.models.BigAutoField" name = "people" + verbose_name = _("People") + + def ready(self): + from people import signals # noqa: F401 diff --git a/people/signals.py b/people/signals.py new file mode 100644 index 0000000..278a38a --- /dev/null +++ b/people/signals.py @@ -0,0 +1,36 @@ +from django.contrib.auth.models import User +from django.db.models.signals import post_save +from django.dispatch import receiver + +from people.models import Person + + +@receiver(post_save, sender=Person) +def create_user_for_person( + sender: Person, instance: Person, created: bool, **kwargs +): + if created and not instance.user: + create_user(instance) + + +def create_user(person: Person): + user, created = User.objects.get_or_create( + username=person.personal_email, + defaults={ + "email": person.personal_email, + "first_name": person.first_name, + "last_name": person.last_name, + "is_staff": True, + }, + ) + if created: + person.user = user + person.save() + + +def deactivate_user(person: Person): + if not person.user_id: + return + person.user.is_staff = False + person.user.is_active = False + person.user.save() From 4345134efe7075ff77dc3b9638e9cc99d759a18d Mon Sep 17 00:00:00 2001 From: Fer-Bar Date: Sat, 25 May 2024 21:33:44 -0400 Subject: [PATCH 09/33] Added LOGIN_REDIRECT_URL and LOGOUT_REDIRECT_URL urls --- albercan_backend/settings.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/albercan_backend/settings.py b/albercan_backend/settings.py index 6e2ad27..ae95c5b 100644 --- a/albercan_backend/settings.py +++ b/albercan_backend/settings.py @@ -143,3 +143,6 @@ # https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" + +LOGIN_REDIRECT_URL = "/index" +LOGOUT_REDIRECT_URL = "/account/login" From 5af33095f0e77ff71dce1a76531b1faf99ceeb40 Mon Sep 17 00:00:00 2001 From: Fer-Bar Date: Sat, 25 May 2024 21:34:33 -0400 Subject: [PATCH 10/33] Added navbar.css styles to base.html --- albercan_backend/templates/base.html | 1 + 1 file changed, 1 insertion(+) diff --git a/albercan_backend/templates/base.html b/albercan_backend/templates/base.html index 4fdfd01..d68a3c2 100644 --- a/albercan_backend/templates/base.html +++ b/albercan_backend/templates/base.html @@ -20,6 +20,7 @@ + {% block styles %}{% endblock %} From 35f4c1a99ff1bfdd8a043632e07cb384ba4e8c35 Mon Sep 17 00:00:00 2001 From: Fer-Bar Date: Sat, 25 May 2024 23:27:38 -0400 Subject: [PATCH 11/33] Added logout link into navbar --- albercan_backend/static/css/navbar.css | 9 +++++++++ albercan_backend/templates/navbar.html | 8 +++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/albercan_backend/static/css/navbar.css b/albercan_backend/static/css/navbar.css index 726198b..7e890b1 100644 --- a/albercan_backend/static/css/navbar.css +++ b/albercan_backend/static/css/navbar.css @@ -71,4 +71,13 @@ header .navbar .dropdown-menu .dropdown-item.active:hover { header .navbar .dropdown-menu .dropdown-item.active { background: #bc5148; color: #fff; +} + +#logout-form { + display: inline; +} +#logout-form button { + background: none; + border: none; + cursor: pointer; } \ No newline at end of file diff --git a/albercan_backend/templates/navbar.html b/albercan_backend/templates/navbar.html index c15d4cd..00c9eb6 100644 --- a/albercan_backend/templates/navbar.html +++ b/albercan_backend/templates/navbar.html @@ -1,7 +1,7 @@