From 1a26d459f659752e8a2ffbff84e9e4d3dd2ea5cb Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 10 Mar 2025 14:39:48 +0100 Subject: [PATCH 1/4] better scroll margin and anchor for interest --- hub/services/templates/services/offering_detail.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hub/services/templates/services/offering_detail.html b/hub/services/templates/services/offering_detail.html index 545d319..391c99d 100644 --- a/hub/services/templates/services/offering_detail.html +++ b/hub/services/templates/services/offering_detail.html @@ -142,7 +142,7 @@ {% endif %} -
+

Available Plans

{% for plan in offering.plans.all %} @@ -169,7 +169,7 @@
{% empty %} -
+

No plans available yet.

I'm interested in this offering

From ac19c4cd25ee886af7ef0d8a509f1bee784ed8cd Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 10 Mar 2025 14:42:34 +0100 Subject: [PATCH 2/4] show coming soon when no plan available --- hub/services/templates/services/provider_detail.html | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hub/services/templates/services/provider_detail.html b/hub/services/templates/services/provider_detail.html index 8998c6a..7292c4d 100644 --- a/hub/services/templates/services/provider_detail.html +++ b/hub/services/templates/services/provider_detail.html @@ -157,6 +157,11 @@
{% endif %} + {% if not offering.plans.all %} +
+ Coming Soon +
+ {% endif %}
{% endif %}
From 38ab1f3b2a7f1bcee81544481ec69c0be7c80ef2 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 10 Mar 2025 14:54:48 +0100 Subject: [PATCH 3/4] link to offering when filter for cloud provider --- .../templates/services/service_list.html | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/hub/services/templates/services/service_list.html b/hub/services/templates/services/service_list.html index 25a0bf6..4b11cc6 100644 --- a/hub/services/templates/services/service_list.html +++ b/hub/services/templates/services/service_list.html @@ -155,9 +155,19 @@
{% if service.logo %}
- - {{ service.name }} logo - + {% if request.GET.cloud_provider %} + {% for offering in service.offerings.all %} + {% if offering.cloud_provider.id|stringformat:'i' == request.GET.cloud_provider %} + + {{ service.name }} logo + + {% endif %} + {% endfor %} + {% else %} + + {{ service.name }} logo + + {% endif %}
{% endif %} {% if service.is_featured %} @@ -173,7 +183,17 @@ {% endif %}
-

{{ service.name }}

+

+ {% if request.GET.cloud_provider %} + {% for offering in service.offerings.all %} + {% if offering.cloud_provider.id|stringformat:'i' == request.GET.cloud_provider %} + {{ service.name }} + {% endif %} + {% endfor %} + {% else %} + {{ service.name }} + {% endif %} +

{% for category in service.categories.all %} {{ category.full_path }} From faa07a8b4d2c783056efccb3d5a84577bd406ae2 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 10 Mar 2025 15:03:20 +0100 Subject: [PATCH 4/4] overhaul service list filtering --- hub/services/views/services.py | 151 ++++++++++++++++++--------------- 1 file changed, 82 insertions(+), 69 deletions(-) diff --git a/hub/services/views/services.py b/hub/services/views/services.py index adb7e22..fc6d275 100644 --- a/hub/services/views/services.py +++ b/hub/services/views/services.py @@ -17,7 +17,8 @@ def service_list(request): # Start with all active services # Filter out services with disable_listing=True - services = Service.objects.filter(disable_listing=False) + all_services = Service.objects.filter(disable_listing=False) + services = all_services # Apply filters based on request parameters if search_query: @@ -42,86 +43,98 @@ def service_list(request): "name", # Alphabetically within each group ) - # Get all available categories from filtered services - available_category_ids = services.values_list( - "categories__id", flat=True + # Create base querysets for each filter type that apply all OTHER current filters + # This way, each filter shows options that would return results if selected + + # For category filter options, apply all other filters except category + category_filter_base = all_services + if search_query: + category_filter_base = category_filter_base.filter( + Q(name__icontains=search_query) | Q(description__icontains=search_query) + ) + if consulting_partner_id: + category_filter_base = category_filter_base.filter( + consulting_partners__id=consulting_partner_id + ) + if cloud_provider_id: + category_filter_base = category_filter_base.filter( + offerings__cloud_provider__id=cloud_provider_id + ) + + # For consulting partner filter options, apply all other filters except consulting_partner + cp_filter_base = all_services + if search_query: + cp_filter_base = cp_filter_base.filter( + Q(name__icontains=search_query) | Q(description__icontains=search_query) + ) + if category_id: + cp_filter_base = cp_filter_base.filter(categories__id=category_id) + if cloud_provider_id: + cp_filter_base = cp_filter_base.filter( + offerings__cloud_provider__id=cloud_provider_id + ) + + # For cloud provider filter options, apply all other filters except cloud_provider + cloud_filter_base = all_services + if search_query: + cloud_filter_base = cloud_filter_base.filter( + Q(name__icontains=search_query) | Q(description__icontains=search_query) + ) + if category_id: + cloud_filter_base = cloud_filter_base.filter(categories__id=category_id) + if consulting_partner_id: + cloud_filter_base = cloud_filter_base.filter( + consulting_partners__id=consulting_partner_id + ) + + # Get available categories that would return results if selected + available_parent_categories = Category.objects.filter( + parent=None, + id__in=category_filter_base.values_list( + "categories__parent__id", flat=True + ).distinct(), + ).distinct() + + # Add categories that are direct matches (not children) + direct_categories = Category.objects.filter( + parent=None, + id__in=category_filter_base.values_list("categories__id", flat=True).distinct(), + ).distinct() + + available_parent_categories = ( + available_parent_categories | direct_categories ).distinct() - available_categories = Category.objects.filter( - id__in=available_category_ids, parent=None - ) # For each parent category, get available children - for category in available_categories: - child_ids = ( - services.filter(categories__parent=category) - .values_list("categories__id", flat=True) - .distinct() - ) - category.available_children = Category.objects.filter(id__in=child_ids) + for parent_category in available_parent_categories: + parent_category.available_children = Category.objects.filter( + parent=parent_category, + id__in=category_filter_base.values_list( + "categories__id", flat=True + ).distinct(), + ).distinct() - # Get available consulting partners from filtered services - # Excluding partners with disable_listing=True - available_consulting_partner_ids = services.values_list( - "consulting_partners__id", flat=True - ).distinct() + # Get available consulting partners and cloud providers that would return results if selected available_consulting_partners = ConsultingPartner.objects.filter( - id__in=available_consulting_partner_ids, disable_listing=False - ) - - # Get available cloud providers from filtered services via offerings - # Excluding providers with disable_listing=True - available_cloud_provider_ids = services.values_list( - "offerings__cloud_provider__id", flat=True + disable_listing=False, + id__in=cp_filter_base.values_list( + "consulting_partners__id", flat=True + ).distinct(), ).distinct() + available_cloud_providers = CloudProvider.objects.filter( - id__in=available_cloud_provider_ids, disable_listing=False - ) - - # For the current selection, we need to make sure we include the selected items - # even if they don't match other filters - if category_id: - try: - selected_category = Category.objects.get(id=category_id) - if selected_category.parent: - parent_category = selected_category.parent - if parent_category not in available_categories: - available_categories = list(available_categories) - available_categories.append(parent_category) - parent_category.available_children = [selected_category] - elif selected_category not in available_categories: - available_categories = list(available_categories) - available_categories.append(selected_category) - except Category.DoesNotExist: - pass - - if consulting_partner_id: - try: - cp_id = int(consulting_partner_id) - if cp_id not in available_consulting_partner_ids: - selected_partner = ConsultingPartner.objects.get(id=cp_id) - available_consulting_partners = list(available_consulting_partners) - available_consulting_partners.append(selected_partner) - except (ValueError, ConsultingPartner.DoesNotExist): - pass - - if cloud_provider_id: - try: - cp_id = int(cloud_provider_id) - if cp_id not in available_cloud_provider_ids: - selected_provider = CloudProvider.objects.get(id=cp_id) - available_cloud_providers = list(available_cloud_providers) - available_cloud_providers.append(selected_provider) - except (ValueError, CloudProvider.DoesNotExist): - pass + disable_listing=False, + id__in=cloud_filter_base.values_list( + "offerings__cloud_provider__id", flat=True + ).distinct(), + ).distinct() context = { "services": services, - "categories": Category.objects.filter(parent=None), - "consulting_partners": ConsultingPartner.objects.filter(disable_listing=False), - "cloud_providers": CloudProvider.objects.filter(disable_listing=False), - "available_categories": available_categories, + "available_categories": available_parent_categories, "available_consulting_partners": available_consulting_partners, "available_cloud_providers": available_cloud_providers, + "search_query": search_query, } return render(request, "services/service_list.html", context)