From 8f840d7c4c808f2716ad3f16efbf8d31b6dc9117 Mon Sep 17 00:00:00 2001 From: Adan Perez Rodriguez Date: Mon, 30 Mar 2026 19:58:17 +0100 Subject: [PATCH 1/5] Task-1 feat: Add search functionality and display search results in room list --- pms/templates/rooms.html | 49 +++++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/pms/templates/rooms.html b/pms/templates/rooms.html index c30929f1f..8c4b160fa 100644 --- a/pms/templates/rooms.html +++ b/pms/templates/rooms.html @@ -2,18 +2,45 @@ {% block content %}

Habitaciones del hotel

-{% for room in rooms%} -
-
-
- {{room.name}} ({{room.room_type__name}}) -
-
- Ver detalles + + +
+
+
+
+
+ {{ form.search }} +
+
+ + {% if search_query %} + Limpiar + {% endif %} +
+
+
+ {% if search_query %} +
+ Buscando: "{{ search_query }}" - {{ rooms|length }} resultado(s) encontrado(s) +
+ {% endif %} +
+
+ + +{% if rooms %} + {% for room in rooms%} +
+
+
+ {{room.name}} ({{room.room_type__name}}) +
+ +
- -
-{% endfor %} + {% endfor %} {% endblock content%} From e8a47ee39554a2f2d83696859bcb6e0dcf215c08 Mon Sep 17 00:00:00 2001 From: Adan Perez Rodriguez Date: Mon, 30 Mar 2026 20:00:38 +0100 Subject: [PATCH 2/5] Task-1 feat: Add message for no rooms found when search query is applied --- pms/templates/rooms.html | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pms/templates/rooms.html b/pms/templates/rooms.html index 8c4b160fa..cfb9d78c2 100644 --- a/pms/templates/rooms.html +++ b/pms/templates/rooms.html @@ -43,4 +43,9 @@

Habitaciones del hotel

{% endfor %} +{% else %} +
+ No se encontraron habitaciones{% if search_query %} que coincidan con "{{ search_query }}"{% endif %}. +
+{% endif %} {% endblock content%} From b3d046fd252217e3ec09f1b04147dedee0a30ef0 Mon Sep 17 00:00:00 2001 From: Adan Perez Rodriguez Date: Mon, 30 Mar 2026 20:02:05 +0100 Subject: [PATCH 3/5] Tastk-1 feat: Add RoomFilterForm for room search functionality --- pms/forms.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/pms/forms.py b/pms/forms.py index f1bc68d08..c48be65ca 100644 --- a/pms/forms.py +++ b/pms/forms.py @@ -2,7 +2,7 @@ from django import forms from django.forms import ModelForm -from .models import Booking, Customer +from .models import Booking, Customer, Room class RoomSearchForm(ModelForm): @@ -56,3 +56,15 @@ class Meta: 'total': forms.HiddenInput(), 'state': forms.HiddenInput(), } + + +class RoomFilterForm(forms.Form): + search = forms.CharField( + required=False, + label='Buscar habitación', + max_length=100, + widget=forms.TextInput(attrs={ + 'class': 'form-control', + 'placeholder': 'Ej: Room 1' + }) + ) From 845359b5a35a0c14311a24d0de98d78cb2f7d338 Mon Sep 17 00:00:00 2001 From: Adan Perez Rodriguez Date: Mon, 30 Mar 2026 20:06:46 +0100 Subject: [PATCH 4/5] Task-1 feat: Implement room search functionality in RoomsView --- pms/views.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/pms/views.py b/pms/views.py index f38563933..b25464275 100644 --- a/pms/views.py +++ b/pms/views.py @@ -238,9 +238,21 @@ def get(self, request, pk): class RoomsView(View): def get(self, request): - # renders a list of rooms - rooms = Room.objects.all().values("name", "room_type__name", "id") + # Get search query from GET parameters (received from the search form in the template) + search_query = request.GET.get('search', '').strip() + + # Filter rooms by name if search query exists + if search_query: + rooms = Room.objects.filter(name__icontains=search_query).values("name", "room_type__name", "id") + else: + rooms = Room.objects.all().values("name", "room_type__name", "id") + + # Initialize form with search query to keep it in the input field + form = RoomFilterForm(initial={'search': search_query}) + context = { - 'rooms': rooms + 'rooms': rooms, + 'form': form, + 'search_query': search_query } return render(request, "rooms.html", context) From 09fcde3d3a74ca919b4016fe2c2312f6045a8b0c Mon Sep 17 00:00:00 2001 From: Adan Perez Rodriguez Date: Mon, 30 Mar 2026 20:19:02 +0100 Subject: [PATCH 5/5] Task-1 test: Add tests for RoomFilterForm and RoomsView filtering functionality --- pms/tests.py | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 137 insertions(+), 2 deletions(-) diff --git a/pms/tests.py b/pms/tests.py index 7ce503c2d..1afb790d2 100644 --- a/pms/tests.py +++ b/pms/tests.py @@ -1,3 +1,138 @@ -from django.test import TestCase +from django.test import TestCase, Client +from django.urls import reverse +from .models import Room, Room_type +from .forms import RoomFilterForm -# Create your tests here. + +class RoomFilterFormTests(TestCase): + """Test cases for RoomFilterForm""" + + def test_form_field_search_is_optional(self): + """Test that search field is not required""" + form = RoomFilterForm(data={'search': ''}) + self.assertTrue(form.is_valid()) + + def test_form_with_valid_search_query(self): + """Test form with valid search query""" + form = RoomFilterForm(data={'search': 'Room 1'}) + self.assertTrue(form.is_valid()) + self.assertEqual(form.cleaned_data['search'], 'Room 1') + + +class RoomsViewFilterTests(TestCase): + """Test cases for RoomsView filtering functionality""" + + @classmethod + def setUpTestData(cls): + """Set up test data for all test methods""" + # Create room types + cls.room_type_individual = Room_type.objects.create( + name='Individual', + price=20.0, + max_guests=1 + ) + cls.room_type_doble = Room_type.objects.create( + name='Doble', + price=30.0, + max_guests=2 + ) + + # Create test rooms + cls.room_1_1 = Room.objects.create( + name='Room 1.1', + description='Habitación individual 1.1', + room_type=cls.room_type_individual + ) + cls.room_1_2 = Room.objects.create( + name='Room 1.2', + description='Habitación individual 1.2', + room_type=cls.room_type_individual + ) + cls.room_2_1 = Room.objects.create( + name='Room 2.1', + description='Habitación doble 2.1', + room_type=cls.room_type_doble + ) + cls.room_2_2 = Room.objects.create( + name='Room 2.2', + description='Habitación doble 2.2', + room_type=cls.room_type_doble + ) + cls.room_3_1 = Room.objects.create( + name='Room 3.1', + description='Habitación individual 3.1', + room_type=cls.room_type_individual + ) + + def setUp(self): + """Set up test client""" + self.client = Client() + self.url = reverse('rooms') + + def test_rooms_view_without_filter_shows_all_rooms(self): + """Test that without filter all rooms are displayed""" + response = self.client.get(self.url) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(response.context['rooms']), 5) + self.assertContains(response, 'Room 1.1') + self.assertContains(response, 'Room 2.1') + self.assertContains(response, 'Room 3.1') + + def test_rooms_view_with_exact_search(self): + """Test exact room name search""" + response = self.client.get(self.url, {'search': 'Room 1.1'}) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(response.context['rooms']), 1) + self.assertContains(response, 'Room 1.1') + self.assertNotContains(response, 'Room 2.1') + + def test_rooms_view_with_partial_search(self): + """Test partial room name search (e.g., 'Room 1' returns 'Room 1.1' and 'Room 1.2')""" + response = self.client.get(self.url, {'search': 'Room 1'}) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(response.context['rooms']), 2) + self.assertContains(response, 'Room 1.1') + self.assertContains(response, 'Room 1.2') + self.assertNotContains(response, 'Room 2.1') + + def test_rooms_view_with_no_results(self): + """Test search with no matching rooms""" + response = self.client.get(self.url, {'search': 'NonExistent'}) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(response.context['rooms']), 0) + self.assertContains(response, 'No se encontraron habitaciones') + + def test_rooms_view_search_is_case_insensitive(self): + """Test that search is case insensitive""" + response = self.client.get(self.url, {'search': 'room 1'}) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(response.context['rooms']), 2) + self.assertContains(response, 'Room 1.1') + self.assertContains(response, 'Room 1.2') + + def test_rooms_view_search_with_whitespace(self): + """Test that search handles whitespace correctly""" + response = self.client.get(self.url, {'search': ' Room 2 '}) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(response.context['rooms']), 2) + self.assertContains(response, 'Room 2.1') + self.assertContains(response, 'Room 2.2') + + def test_rooms_view_empty_search_shows_all_rooms(self): + """Test that empty search parameter shows all rooms""" + response = self.client.get(self.url, {'search': ''}) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(response.context['rooms']), 5) + + def test_rooms_view_contains_search_form(self): + """Test that the view includes the search form""" + response = self.client.get(self.url) + self.assertEqual(response.status_code, 200) + self.assertIn('form', response.context) + self.assertIsInstance(response.context['form'], RoomFilterForm) + + def test_rooms_view_preserves_search_query_in_context(self): + """Test that search query is preserved in context""" + response = self.client.get(self.url, {'search': 'Room 1'}) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.context['search_query'], 'Room 1')