diff --git a/src/servala/core/models/organization.py b/src/servala/core/models/organization.py index be6737e..e842c36 100644 --- a/src/servala/core/models/organization.py +++ b/src/servala/core/models/organization.py @@ -38,6 +38,7 @@ class Organization(ServalaModelMixin, models.Model): class urls(urlman.Urls): base = "/org/{self.slug}/" details = "{base}details/" + services = "{base}services/" @cached_property def slug(self): diff --git a/src/servala/frontend/forms/service.py b/src/servala/frontend/forms/service.py new file mode 100644 index 0000000..6047293 --- /dev/null +++ b/src/servala/frontend/forms/service.py @@ -0,0 +1,22 @@ +from django import forms + +from servala.core.models import CloudProvider, ServiceCategory + + +class ServiceFilterForm(forms.Form): + category = forms.ModelChoiceField( + queryset=ServiceCategory.objects.all(), required=False + ) + cloud_provider = forms.ModelChoiceField( + queryset=CloudProvider.objects.all(), required=False + ) + q = forms.CharField(required=False) + + def filter_queryset(self, queryset): + if category := self.cleaned_data.get("category"): + queryset = queryset.filter(category=category) + if cloud_provider := self.cleaned_data.get("cloud_provider"): + queryset = queryset.filter( + service_offerings__control_planes__cloud_provider=cloud_provider + ) + return queryset diff --git a/src/servala/frontend/views/mixins.py b/src/servala/frontend/views/mixins.py index e27c28d..502a26a 100644 --- a/src/servala/frontend/views/mixins.py +++ b/src/servala/frontend/views/mixins.py @@ -1,6 +1,8 @@ from django.utils.functional import cached_property from django.views.generic import UpdateView -from rules.contrib.views import AutoPermissionRequiredMixin +from rules.contrib.views import AutoPermissionRequiredMixin, PermissionRequiredMixin + +from servala.core.models import Organization class HtmxUpdateView(AutoPermissionRequiredMixin, UpdateView): diff --git a/src/servala/frontend/views/service.py b/src/servala/frontend/views/service.py new file mode 100644 index 0000000..708205e --- /dev/null +++ b/src/servala/frontend/views/service.py @@ -0,0 +1,31 @@ +from django.utils.functional import cached_property +from django.views.generic import ListView + +from servala.core.models import Service +from servala.frontend.forms.service import ServiceFilterForm +from servala.frontend.views.mixins import OrganizationViewMixin + + +class OrganizationServicesView(OrganizationViewMixin, ListView): + """View to display all available services for an organization.""" + + template_name = "frontend/organizations/services.html" + context_object_name = "services" + model = Service + permission_type = "view" + + def get_queryset(self): + """Return all services.""" + services = Service.objects.all().select_related("category") + if self.filter_form.is_valid(): + services = self.filter_form.filter_queryset(services) + return services + + @cached_property + def filter_form(self): + return ServiceFilterForm(data=self.request.GET or None) + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context["filter_form"] = self.filter_form + return context diff --git a/src/servala/static/css/servala.css b/src/servala/static/css/servala.css index 826a458..97d103e 100644 --- a/src/servala/static/css/servala.css +++ b/src/servala/static/css/servala.css @@ -1,3 +1,11 @@ .form-group.d-inline { margin-bottom: 0; } + +.search-form .form-body>.row { + display: flex; + &>.col-12 { + width: auto; + flex-grow: 1; + } +}