Add skeleton service detail page
This commit is contained in:
parent
4fcc9154b6
commit
58790c3b16
5 changed files with 158 additions and 7 deletions
|
@ -0,0 +1,126 @@
|
|||
{% extends "frontend/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
{% block html_title %}
|
||||
{% block page_title %}
|
||||
{{ service.name }}
|
||||
{% endblock page_title %}
|
||||
{% endblock html_title %}
|
||||
{% block content %}
|
||||
<section class="section">
|
||||
<div class="card">
|
||||
<div class="card-header d-flex align-items-center">
|
||||
{% if service.logo %}
|
||||
<img src="{{ service.logo.url }}"
|
||||
alt="{{ service.name }}"
|
||||
class="me-3"
|
||||
style="max-width: 48px;
|
||||
max-height: 48px">
|
||||
{% endif %}
|
||||
<div class="d-flex flex-column">
|
||||
<h4 class="card-title mb-0">{{ service.name }}</h4>
|
||||
<small class="text-muted">{{ service.category }}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<p>{{ service.description|default:"No description available." }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h4 class="card-title">{% trans "Available Offerings" %}</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% for offering in service.offerings.all %}
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-light">
|
||||
<div class="d-flex align-items-center">
|
||||
{% if offering.provider.logo %}
|
||||
<img src="{{ offering.provider.logo.url }}"
|
||||
alt="{{ offering.provider.name }}"
|
||||
class="me-3"
|
||||
style="max-height: 40px">
|
||||
{% endif %}
|
||||
<h5 class="mb-0">{{ service.name }} on {{ offering.provider.name }}</h5>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-8">
|
||||
<p>{{ offering.description|default:"No description available." }}</p>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<h6>{% trans "Control Planes" %}</h6>
|
||||
<ul>
|
||||
{% for control_plane in offering.control_planes.all %}
|
||||
<li>{{ control_plane.name }}</li>
|
||||
{% empty %}
|
||||
<li>{% trans "No control planes available" %}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<h6>{% trans "Available Plans" %}</h6>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Plan" %}</th>
|
||||
<th>{% trans "Term" %}</th>
|
||||
<th>{% trans "Features" %}</th>
|
||||
<th>{% trans "Pricing" %}</th>
|
||||
<th>{% trans "Actions" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for plan in plans %}
|
||||
<tr>
|
||||
<td>
|
||||
<strong>{{ plan.name }}</strong>
|
||||
{% if plan.description %}
|
||||
<br>
|
||||
<small>{{ plan.description }}</small>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ plan.term }} {% trans "months" %}</td>
|
||||
<td>
|
||||
{% if plan.features %}
|
||||
<ul class="mb-0">
|
||||
{% for feature, value in plan.features.items %}<li>{{ feature }}: {{ value }}</li>{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<span class="text-muted">{% trans "No features specified" %}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if plan.pricing %}
|
||||
<ul class="mb-0">
|
||||
{% for price_type, price in plan.pricing.items %}<li>{{ price_type }}: {{ price }}</li>{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<span class="text-muted">{% trans "No pricing specified" %}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-primary btn-sm">{% trans "Order" %}</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="5" class="text-center">{% trans "No plans available for this offering" %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% empty %}
|
||||
<div class="alert alert-info">{% trans "No offerings available for this service yet." %}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock content %}
|
|
@ -22,7 +22,12 @@ urlpatterns = [
|
|||
),
|
||||
path(
|
||||
"services/",
|
||||
views.OrganizationServicesView.as_view(),
|
||||
views.ServiceListView.as_view(),
|
||||
name="organization.services",
|
||||
),
|
||||
path(
|
||||
"services/<int:pk>/",
|
||||
views.ServiceDetailView.as_view(),
|
||||
name="organization.services",
|
||||
),
|
||||
path(
|
||||
|
|
|
@ -5,7 +5,7 @@ from .organization import (
|
|||
OrganizationDashboardView,
|
||||
OrganizationUpdateView,
|
||||
)
|
||||
from .service import OrganizationServicesView
|
||||
from .service import ServiceDetailView, ServiceListView
|
||||
|
||||
__all__ = [
|
||||
"IndexView",
|
||||
|
@ -13,6 +13,7 @@ __all__ = [
|
|||
"OrganizationCreateView",
|
||||
"OrganizationDashboardView",
|
||||
"OrganizationUpdateView",
|
||||
"OrganizationServicesView",
|
||||
"ServiceDetailView",
|
||||
"ServiceListView",
|
||||
"ProfileView",
|
||||
]
|
||||
|
|
|
@ -71,7 +71,9 @@ class OrganizationViewMixin(PermissionRequiredMixin):
|
|||
return self.request.organization
|
||||
|
||||
def get_object(self):
|
||||
if self.model == Organization:
|
||||
return self.organization
|
||||
return super().get_object()
|
||||
|
||||
@cached_property
|
||||
def object(self):
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
from django.utils.functional import cached_property
|
||||
from django.views.generic import ListView
|
||||
from django.views.generic import DetailView, ListView
|
||||
|
||||
from servala.core.models import Service
|
||||
from servala.core.models import Plan, Service, ServiceOffering
|
||||
from servala.frontend.forms.service import ServiceFilterForm
|
||||
from servala.frontend.views.mixins import OrganizationViewMixin
|
||||
|
||||
|
||||
class OrganizationServicesView(OrganizationViewMixin, ListView):
|
||||
class ServiceListView(OrganizationViewMixin, ListView):
|
||||
"""View to display all available services for an organization."""
|
||||
|
||||
template_name = "frontend/organizations/services.html"
|
||||
|
@ -29,3 +29,20 @@ class OrganizationServicesView(OrganizationViewMixin, ListView):
|
|||
context = super().get_context_data(**kwargs)
|
||||
context["filter_form"] = self.filter_form
|
||||
return context
|
||||
|
||||
|
||||
class ServiceDetailView(OrganizationViewMixin, DetailView):
|
||||
"""View to display details of a specific service and its offerings."""
|
||||
|
||||
template_name = "frontend/organizations/service_detail.html"
|
||||
context_object_name = "service"
|
||||
model = Service
|
||||
permission_type = "view"
|
||||
|
||||
def get_queryset(self):
|
||||
return Service.objects.select_related("category").prefetch_related(
|
||||
"offerings",
|
||||
"offerings__provider",
|
||||
"offerings__control_planes",
|
||||
"offerings__plans",
|
||||
)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue