make whole card clickable
All checks were successful
Build and Deploy / build (push) Successful in 36s
Build and Deploy / deploy (push) Successful in 4s

This commit is contained in:
Tobias Brunner 2025-05-07 08:36:02 +02:00
parent b1680a11af
commit 1911a1211b
No known key found for this signature in database
7 changed files with 126 additions and 84 deletions

View file

@ -12322,4 +12322,20 @@ a.btn:focus {
.page-content .page-action a { .page-content .page-action a {
text-decoration: none text-decoration: none
}
.clickable-card {
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
position: relative;
}
.clickable-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
}
.clickable-button {
position: relative;
z-index: 2;
} }

View file

@ -0,0 +1,9 @@
function cardClicked(event, url) {
// Don't navigate if the click was on a button or link
if (event.target.closest('.clickable-button') || event.target.closest('.clickable-link')) {
return;
}
// Navigate to the provider detail page
window.location.href = url;
}

View file

@ -23,6 +23,7 @@
<script defer src="{% static "js/htmx204.min.js" %}"></script> <script defer src="{% static "js/htmx204.min.js" %}"></script>
<script defer src="{% static "js/alpine-collapse.min.js" %}"></script> <script defer src="{% static "js/alpine-collapse.min.js" %}"></script>
<script defer src="{% static "js/servala-main.js" %}"></script> <script defer src="{% static "js/servala-main.js" %}"></script>
<script defer src="{% static "js/servala-addons.js" %}"></script>
{% block extra_js %}{% endblock %} {% block extra_js %}{% endblock %}
</head> </head>
<body> <body>

View file

@ -41,20 +41,31 @@
<div class="row"> <div class="row">
{% for service in featured_services %} {% for service in featured_services %}
<div class="col-12 col-md-6 col-lg-3 mb-20 mb-lg-0"> <div class="col-12 col-md-6 col-lg-3 mb-20 mb-lg-0">
<div class="card h-100 d-flex flex-column"> <div class="card h-100 d-flex flex-column clickable-card"
<div class="card__image"> onclick="cardClicked(event, '{{ service.get_absolute_url }}')">
<a href="{{ service.get_absolute_url }}"><img class="img-fluid card-logo" src="{{ service.logo.url }}" alt="{{ service.name }} logo"></a> <div class="card__content d-flex flex-column flex-grow-1">
<div class="card__header">
<div class="d-flex align-items-start" style="height: 100px; margin-bottom: 1rem;">
<div class="me-3 d-flex align-items-center" style="height: 100%;">
<a href="{{ service.get_absolute_url }}" class="clickable-link">
<img src="{{ service.logo.url }}"
alt="{{ service.name }}"
style="max-height: 100px; max-width: 250px; object-fit: contain;">
</a>
</div>
</div>
<h3 class="card__title">
<a href="{{ service.get_absolute_url }}" class="text-decoration-none clickable-link">{{ service.name }}</a>
</h3>
{% for category in service.categories.all|slice:":1" %}
<p class="card__subtitle">{{ category.name }}</p>
{% endfor %}
</div>
<div class="card__desc flex-grow-1">
<p>{{ service.description|safe|truncatewords:15 }}</p>
</div>
</div>
</div> </div>
<div class="card__header">
<h3 class="card__title"><a href="{{ service.get_absolute_url }}" class="text-decoration-none">{{ service.name }}</a></h3>
{% for category in service.categories.all|slice:":1" %}
<p class="card__subtitle">{{ category.name }}</p>
{% endfor %}
</div>
<div class="card__desc flex-grow-1">
<p>{{ service.description|safe|truncatewords:15 }}</p>
</div>
</div>
</div> </div>
{% empty %} {% empty %}
<div class="col-12"> <div class="col-12">
@ -85,29 +96,36 @@
</header> </header>
<div class="section__grid"> <div class="section__grid">
<div class="row"> <div class="row">
<div class="section__grid"> {% for provider in featured_providers %}
<div class="row"> <div class="col-12 col-md-6 col-lg-3 mb-20 mb-lg-0">
{% for provider in featured_providers %} <div class="card h-100 d-flex flex-column clickable-card"
<div class="col-12 col-md-6 col-lg-3 mb-20 mb-lg-0"> onclick="cardClicked(event, '{{ provider.get_absolute_url }}')">
<div class="card h-100 d-flex flex-column"> <div class="card__content d-flex flex-column flex-grow-1">
<div class="card__image__wide mb-4">
<a href="{{ provider.get_absolute_url }}"><img class="img-fluid card-logo" src="{{ provider.logo.url }}" alt="{{ provider.name }} logo"></a>
</div>
<div class="card__header"> <div class="card__header">
<h3 class="card__title"><a href="{{ provider.get_absolute_url }}" class="text-decoration-none">{{ provider.name }}</a></h3> <div class="d-flex align-items-start" style="height: 100px; margin-bottom: 1rem;">
<div class="me-3 d-flex align-items-center" style="height: 100%;">
<a href="{{ provider.get_absolute_url }}" class="clickable-link">
<img src="{{ provider.logo.url }}"
alt="{{ provider.name }}"
style="max-height: 100px; max-width: 250px; object-fit: contain;">
</a>
</div>
</div>
<h3 class="card__title">
<a href="{{ provider.get_absolute_url }}" class="text-decoration-none clickable-link">{{ provider.name }}</a>
</h3>
</div> </div>
<div class="card__desc flex-grow-1"> <div class="card__desc flex-grow-1">
<p>{{ provider.description|safe|truncatewords:15 }}</p> <p>{{ provider.description|safe|truncatewords:15 }}</p>
</div> </div>
</div> </div>
</div> </div>
{% empty %}
<div class="col-12">
<p class="text-center">No featured provider available at the moment.</p>
</div>
{% endfor %}
</div> </div>
</div> {% empty %}
<div class="col-12">
<p class="text-center">No featured provider available at the moment.</p>
</div>
{% endfor %}
</div> </div>
</div> </div>
<div class="page-action d-lg-none"> <div class="page-action d-lg-none">
@ -132,25 +150,36 @@
</header> </header>
<div class="section__grid"> <div class="section__grid">
<div class="row"> <div class="row">
{% for partner in featured_partners %} {% for partner in featured_partners %}
<div class="col-12 col-md-6 col-lg-3 mb-20 mb-lg-0"> <div class="col-12 col-md-6 col-lg-3 mb-20 mb-lg-0">
<div class="card h-100 d-flex flex-column"> <div class="card h-100 d-flex flex-column clickable-card"
<div class="card__image__wide mb-4"> onclick="cardClicked(event, '{{ partner.get_absolute_url }}')">
<a href="{{ partner.get_absolute_url }}"><img class="img-fluid card-logo" src="{{ partner.logo.url }}" alt="{{ partner.name }} logo"></a> <div class="card__content d-flex flex-column flex-grow-1">
</div> <div class="card__header">
<div class="card__header"> <div class="d-flex align-items-start" style="height: 100px; margin-bottom: 1rem;">
<h3 class="card__title"><a href="{{ partner.get_absolute_url }}" class="text-decoration-none">{{ partner.name }}</a></h3> <div class="me-3 d-flex align-items-center" style="height: 100%;">
</div> <a href="{{ partner.get_absolute_url }}" class="clickable-link">
<div class="card__desc flex-grow-1"> <img src="{{ partner.logo.url }}"
<p>{{ partner.description|safe|truncatewords:15 }}</p> alt="{{ partner.name }}"
style="max-height: 100px; max-width: 250px; object-fit: contain;">
</a>
</div>
</div>
<h3 class="card__title">
<a href="{{ partner.get_absolute_url }}" class="text-decoration-none clickable-link">{{ partner.name }}</a>
</h3>
</div>
<div class="card__desc flex-grow-1">
<p>{{ partner.description|safe|truncatewords:15 }}</p>
</div>
</div>
</div> </div>
</div> </div>
</div> {% empty %}
{% empty %}
<div class="col-12"> <div class="col-12">
<p class="text-center">No featured partner available at the moment.</p> <p class="text-center">No featured partner available at the moment.</p>
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
</div> </div>
<div class="page-action d-lg-none"> <div class="page-action d-lg-none">
@ -189,7 +218,7 @@
<header class="section__header text-center mb-30"> <header class="section__header text-center mb-30">
<h2 class="section-heading-h2 fs-40 fs-lg-64">Frequenty Asked Questions</h2> <h2 class="section-heading-h2 fs-40 fs-lg-64">Frequenty Asked Questions</h2>
<div class="section__desc fw-medium fs-20 w-lg-40 mx-auto"> <div class="section__desc fw-medium fs-20 w-lg-40 mx-auto">
<p>Still need more information? Read our FAQ or contact us to learn more about the Servala.</p> <p>Still need more information? Read our FAQ or contact us to learn more about the Servala.</p>
</div> </div>
</header> </header>
<div x-data="{ open: null }" class="section__faq"> <div x-data="{ open: null }" class="section__faq">
@ -215,7 +244,6 @@
</div> </div>
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
</div> </div>
</section> </section>

View file

@ -108,12 +108,13 @@
<div class="row"> <div class="row">
{% for partner in partners %} {% for partner in partners %}
<div class="col-12 col-md-6 col-lg-4 mb-30"> <div class="col-12 col-md-6 col-lg-4 mb-30">
<div class="card h-100 d-flex flex-column"> <div class="card h-100 d-flex flex-column clickable-card"
onclick="cardClicked(event, '{{ partner.get_absolute_url }}')">
<div class="card__content d-flex flex-column flex-grow-1"> <div class="card__content d-flex flex-column flex-grow-1">
<div class="card__header"> <div class="card__header">
<div class="d-flex align-items-start" style="height: 100px; margin-bottom: 1rem;"> <div class="d-flex align-items-start" style="height: 100px; margin-bottom: 1rem;">
<div class="me-3"> <div class="me-3">
<a href="{{ partner.get_absolute_url }}"> <a href="{{ partner.get_absolute_url }}" class="clickable-link">
<img src="{{ partner.logo.url }}" <img src="{{ partner.logo.url }}"
alt="{{ partner.name }}" alt="{{ partner.name }}"
style="max-height: 100px; max-width: 250px; object-fit: contain;"> style="max-height: 100px; max-width: 250px; object-fit: contain;">
@ -121,7 +122,7 @@
</div> </div>
</div> </div>
<h3 class="card__title"> <h3 class="card__title">
<a href="{{ partner.get_absolute_url }}" class="text-decoration-none">{{ partner.name }}</a> <a href="{{ partner.get_absolute_url }}" class="text-decoration-none clickable-link">{{ partner.name }}</a>
</h3> </h3>
</div> </div>
@ -132,9 +133,9 @@
<div class="card__footer mt-3"> <div class="card__footer mt-3">
<div class="d-flex gap-2"> <div class="d-flex gap-2">
{% if partner.website %} {% if partner.website %}
<a href="{{ partner.website }}" class="btn btn-primary btn-sm" target="_blank">Visit Website</a> <a href="{{ partner.website }}" class="btn btn-primary btn-sm clickable-button" target="_blank">Visit Website</a>
{% endif %} {% endif %}
<a href="{{ partner.get_absolute_url }}#services" class="btn btn-primary btn-sm">Available Services</a> <a href="{{ partner.get_absolute_url }}#services" class="btn btn-primary btn-sm clickable-button">Available Services</a>
</div> </div>
</div> </div>
</div> </div>

View file

@ -91,12 +91,13 @@
<div class="row"> <div class="row">
{% for provider in providers %} {% for provider in providers %}
<div class="col-12 col-md-6 col-lg-4 mb-30"> <div class="col-12 col-md-6 col-lg-4 mb-30">
<div class="card h-100 d-flex flex-column"> <div class="card h-100 d-flex flex-column clickable-card"
onclick="cardClicked(event, '{{ provider.get_absolute_url }}')">
<div class="card__content d-flex flex-column flex-grow-1"> <div class="card__content d-flex flex-column flex-grow-1">
<div class="card__header"> <div class="card__header">
<div class="d-flex align-items-start" style="height: 100px; margin-bottom: 1rem;"> <div class="d-flex align-items-start" style="height: 100px; margin-bottom: 1rem;">
<div class="me-3 d-flex align-items-center" style="height: 100%;"> <div class="me-3 d-flex align-items-center" style="height: 100%;">
<a href="{{ provider.get_absolute_url }}"> <a href="{{ provider.get_absolute_url }}" class="clickable-link">
<img src="{{ provider.logo.url }}" <img src="{{ provider.logo.url }}"
alt="{{ provider.name }}" alt="{{ provider.name }}"
style="max-height: 100px; max-width: 250px; object-fit: contain;"> style="max-height: 100px; max-width: 250px; object-fit: contain;">
@ -104,7 +105,7 @@
</div> </div>
</div> </div>
<h3 class="card__title"> <h3 class="card__title">
<a href="{{ provider.get_absolute_url }}" class="text-decoration-none">{{ provider.name }}</a> <a href="{{ provider.get_absolute_url }}" class="text-decoration-none clickable-link">{{ provider.name }}</a>
</h3> </h3>
</div> </div>
@ -115,9 +116,9 @@
<div class="card__footer mt-3"> <div class="card__footer mt-3">
<div class="d-flex gap-2"> <div class="d-flex gap-2">
{% if provider.website %} {% if provider.website %}
<a href="{{ provider.website }}" class="btn btn-primary btn-sm" target="_blank">Visit Website</a> <a href="{{ provider.website }}" class="btn btn-primary btn-sm clickable-button" target="_blank">Visit Website</a>
{% endif %} {% endif %}
<a href="{{ provider.get_absolute_url }}#services" class="btn btn-primary btn-sm">Available Services</a> <a href="{{ provider.get_absolute_url }}#services" class="btn btn-primary btn-sm clickable-button">Available Services</a>
</div> </div>
</div> </div>
</div> </div>

View file

@ -150,49 +150,30 @@
<div class="row"> <div class="row">
{% for service in services %} {% for service in services %}
<div class="col-12 col-md-6 col-lg-4 mb-30"> <div class="col-12 col-md-6 col-lg-4 mb-30">
<div class="card {% if service.is_featured %}card-featured{% endif %} h-100 d-flex flex-column"> <div class="card {% if service.is_featured %}card-featured{% endif %} h-100 d-flex flex-column clickable-card"
onclick="cardClicked(event, '{% if request.GET.cloud_provider %}{% for offering in service.offerings.all %}{% if offering.cloud_provider.id|stringformat:"i" == request.GET.cloud_provider %}{% url "services:offering_detail" offering.cloud_provider.slug service.slug %}{% endif %}{% endfor %}{% else %}{{ service.get_absolute_url }}{% endif %}')">
{% if service.logo or service.is_featured or service.is_coming_soon %} {% if service.logo or service.is_featured or service.is_coming_soon %}
<div class="d-flex justify-content-between"> <div class="d-flex justify-content-between mb-3">
{% if service.logo %} {% if service.logo %}
<div class="card__image flex-shrink-0"> <div class="card__image flex-shrink-0">
{% if request.GET.cloud_provider %} <img src="{{ service.logo.url }}" alt="{{ service.name }} logo" class="img-fluid">
{% for offering in service.offerings.all %}
{% if offering.cloud_provider.id|stringformat:'i' == request.GET.cloud_provider %}
<a href="{% url 'services:offering_detail' offering.cloud_provider.slug service.slug %}">
<img src="{{ service.logo.url }}" alt="{{ service.name }} logo" class="img-fluid">
</a>
{% endif %}
{% endfor %}
{% else %}
<a href="{{ service.get_absolute_url }}">
<img src="{{ service.logo.url }}" alt="{{ service.name }} logo" class="img-fluid">
</a>
{% endif %}
</div> </div>
{% endif %} {% endif %}
{% if service.is_featured %} {% if service.is_featured %}
<div> <div>
<a class="btn btn-primary btn-sm" href="">Featured</a> <span class="btn btn-primary btn-sm">Featured</span>
</div> </div>
{% elif service.is_coming_soon %} {% elif service.is_coming_soon %}
<div> <div>
<a class="btn btn-secondary btn-sm" href="">Coming Soon</a> <span class="btn btn-secondary btn-sm">Coming Soon</span>
</div> </div>
{% endif %} {% endif %}
</div> </div>
{% endif %} {% endif %}
<div class="card__content d-flex flex-column flex-grow-1"> <div class="card__content d-flex flex-column flex-grow-1">
<div class="card__header"> <div class="card__header">
<h3 class="card__title"> <h3 class="card__title {% if service.is_coming_soon %}text-black-50{% endif %}">
{% if request.GET.cloud_provider %} {{ service.name }}
{% for offering in service.offerings.all %}
{% if offering.cloud_provider.id|stringformat:'i' == request.GET.cloud_provider %}
<a href="{% url 'services:offering_detail' offering.cloud_provider.slug service.slug %}" class="text-decoration-none {% if service.is_coming_soon %}text-black-50{% endif %}">{{ service.name }}</a>
{% endif %}
{% endfor %}
{% else %}
<a href="{{ service.get_absolute_url }}" class="text-decoration-none {% if service.is_coming_soon %}text-black-50{% endif %}">{{ service.name }}</a>
{% endif %}
</h3> </h3>
<p class="card__subtitle"> <p class="card__subtitle">
{% for category in service.categories.all %} {% for category in service.categories.all %}
@ -211,8 +192,13 @@
</div> </div>
</div> </div>
</div> </div>
{% empty %}
<div class="col-12">
<div class="alert alert-info">
No services found matching your criteria.
</div>
</div>
{% endfor %} {% endfor %}
</div> </div>
</div> </div>
</div> </div>