From 04e3cc3ef438573f7233c2cc0485d842a5db265f Mon Sep 17 00:00:00 2001 From: AdrianCabreraPhi Date: Wed, 25 Mar 2026 12:23:11 +0100 Subject: [PATCH 1/3] feat(rooms): Add search filters to the room list view --- pms/views.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pms/views.py b/pms/views.py index f38563933..df7943014 100644 --- a/pms/views.py +++ b/pms/views.py @@ -240,6 +240,14 @@ class RoomsView(View): def get(self, request): # renders a list of rooms rooms = Room.objects.all().values("name", "room_type__name", "id") + + # get filter query from URL + query = request.GET.get('q') + + # apply prefix filter + if query: + rooms = rooms.filter(name__istartswith=query) + context = { 'rooms': rooms } From b1dc824d8b0caa8924763b2ed7a0833ed844c9dd Mon Sep 17 00:00:00 2001 From: AdrianCabreraPhi Date: Wed, 25 Mar 2026 12:45:09 +0100 Subject: [PATCH 2/3] feat(rooms): A new input field has been added to filter rooms, along with an error message for the user when no rooms match the search filter; the logic has been moved to a separate file named filter_room.js --- pms/statics/js/filter_room.js | 38 +++++++++++++++++++++++++++++++++++ pms/templates/rooms.html | 22 +++++++++++++++++++- 2 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 pms/statics/js/filter_room.js diff --git a/pms/statics/js/filter_room.js b/pms/statics/js/filter_room.js new file mode 100644 index 000000000..ae4dc3463 --- /dev/null +++ b/pms/statics/js/filter_room.js @@ -0,0 +1,38 @@ + +const searchInput = document.getElementById("room-search"); +const container = document.getElementById("room-list-container"); +let timeout = null; + +searchInput.addEventListener("input", function () { + clearTimeout(timeout); + + timeout = setTimeout(() => { + // trim() delete unnecessary whitespaces + const query = searchInput.value.trim(); + + // Prevent unnecessary requests + if (query.length > 0 && query.length < 4) { + return; + } + + fetch(`?q=${query}`) + .then((response) => response.text()) + .then((html) => { + // Parse the received HTML text into a virtual document + const parser = new DOMParser(); + const doc = parser.parseFromString(html, "text/html"); + + // Extract ONLY the content from the room list container + const newContent = doc.getElementById( + "room-list-container", + ).innerHTML; + + // Update the UI + container.innerHTML = newContent; + + // Update URL with rooms filtered + const newURL = query ? `?q=${query}` : window.location.pathname; + window.history.replaceState(null, "", newURL); + }); + }, 300); +}); diff --git a/pms/templates/rooms.html b/pms/templates/rooms.html index c30929f1f..a584b67dd 100644 --- a/pms/templates/rooms.html +++ b/pms/templates/rooms.html @@ -2,6 +2,18 @@ {% block content %}

Habitaciones del hotel

+ +
+ +
+ +
{% for room in rooms%}
@@ -13,7 +25,15 @@

Habitaciones del hotel

-
+ +{% empty %} +
+ No se han encontrado habitaciones. +
{% endfor %} + + +{% load static %} + {% endblock content%} From d8eed9a329250820e4df66aad15d834aff65436d Mon Sep 17 00:00:00 2001 From: AdrianCabreraPhi Date: Wed, 25 Mar 2026 13:19:32 +0100 Subject: [PATCH 3/3] test: implement unit tests for room search filtering --- pms/tests.py | 46 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/pms/tests.py b/pms/tests.py index 7ce503c2d..67878b649 100644 --- a/pms/tests.py +++ b/pms/tests.py @@ -1,3 +1,45 @@ -from django.test import TestCase +from django.test import TestCase,override_settings +from .models import Room, Room_type -# Create your tests here. +@override_settings(STATICFILES_STORAGE='django.contrib.staticfiles.storage.StaticFilesStorage') +class RoomSearchTestCase(TestCase): + def setUp(self): + + self.room_type = Room_type.objects.create(name="Simple", price=20, max_guests=1) + + Room.objects.create(name="Room 1.1", room_type=self.room_type,description="lorem ipsuum") + Room.objects.create(name="Room 1.2", room_type=self.room_type,description="lorem ipsuum") + Room.objects.create(name="Room 2.1", room_type=self.room_type,description="lorem ipsuum") + Room.objects.create(name="Room 4.1", room_type=self.room_type,description="lorem ipsuum") + + + def test_search_no_query_returns_all(self): + """ + case: no query param introduced + expect: return all rooms available in this case 4 rooms + """ + response = self.client.get('/rooms/') + + self.assertEqual(response.status_code, 200) + self.assertEqual(len(response.context['rooms']), 4) + + def test_search_prefix_match(self): + """ + case: user introduce Room 1 + expect: Room 1.1 & Room 1.2 + """ + response = self.client.get('/rooms/?q=Room 1') + + self.assertEqual(response.status_code, 200) + self.assertEqual(len(response.context['rooms']), 2) + + def test_search_no_match(self): + """ + case: user introduce invalid room name + expect: 0 rooms + """ + + response = self.client.get('/rooms/?q=Example') + + self.assertEqual(response.status_code, 200) + self.assertEqual(len(response.context['rooms']), 0)