This commit is contained in:
Tobias Brunner 2025-06-06 14:53:49 +02:00
parent 01d35a461b
commit d1926cfc17
No known key found for this signature in database
12 changed files with 862 additions and 4 deletions

View file

@ -55,6 +55,7 @@
<ul class="navbar__menu menu mr-lg-27">
<li class="menu__item"><a class="menu__item-link" href="{% url 'services:homepage' %}">Home</a></li>
<li class="menu__item"><a class="menu__item-link" href="{% url 'services:service_list' %}">Services</a></li>
<li class="menu__item"><a class="menu__item-link" href="{% url 'services:article_list' %}">Articles</a></li>
<li class="menu__item"><a class="menu__item-link" href="{% url 'services:provider_list' %}">Cloud Providers</a></li>
<li class="menu__item"><a class="menu__item-link" href="{% url 'services:partner_list' %}">Consulting Partners</a></li>
<li class="menu__item"><a class="menu__item-link" href="{% url 'services:about' %}">About</a></li>
@ -155,6 +156,7 @@
<li><a href="{% url 'services:homepage' %}">Home</a></li>
<li><a href="{% url 'services:about' %}">About</a></li>
<li><a href="{% url 'services:service_list' %}">Services</a></li>
<li><a href="{% url 'services:article_list' %}">Articles</a></li>
<li><a href="{% url 'services:provider_list' %}">Cloud Providers</a></li>
<li><a href="{% url 'services:partner_list' %}">Consulting Partners</a></li>
</ul>

View file

@ -0,0 +1,160 @@
{% extends 'base.html' %}
{% load static %}
{% load contact_tags %}
{% block title %}{{ article.title }}{% endblock %}
{% block meta_description %}{{ article.excerpt }}{% endblock %}
{% block meta_keywords %}{{ article.meta_keywords }}{% endblock %}
{% block content %}
<section class="section bg-primary-subtle">
<div class="container mx-auto px-20 px-lg-0 pt-40 pb-60">
<header class="section-primary__header text-center">
<h1 class="section-h1 fs-40 fs-lg-64 mb-24">{{ article.title }}</h1>
<div class="text-gray-300 w-lg-50 mx-auto">
<p class="mb-3">{{ article.excerpt }}</p>
<div class="d-flex justify-content-center align-items-center gap-3 text-sm">
<span>By {{ article.author.get_full_name|default:article.author.username }}</span>
<span></span>
<span>{{ article.created_at|date:"M d, Y" }}</span>
{% if article.updated_at != article.created_at %}
{% endif %}
</div>
</div>
</header>
</div>
</section>
{% if article.image %}
<section class="section py-0">
<div class="container-xl mx-auto">
<div class="article-hero-image">
<img src="{{ article.image.url }}" alt="{{ article.title }}" class="img-fluid w-100" style="max-height: 400px; object-fit: cover;">
</div>
</div>
</section>
{% endif %}
<section class="section">
<div class="container-xl mx-auto px-3 px-lg-0 pt-60 pt-lg-80 pb-40">
<div class="row">
<div class="col-12 col-lg-8 mx-auto">
<article class="article-content">
<div class="prose">
{{ article.content|safe }}
</div>
</article>
<!-- Related Links -->
{% if article.related_service or article.related_consulting_partner or article.related_cloud_provider %}
<div class="mt-5 pt-4 border-top">
<h3>Related Links</h3>
<div class="row">
{% if article.related_service %}
<div class="col-12 col-md-4 mb-3">
<div class="card h-100">
<div class="card-body">
<h5 class="card-title">Service</h5>
<p class="card-text">{{ article.related_service.name }}</p>
<a href="{{ article.related_service.get_absolute_url }}" class="btn btn-primary btn-sm">View Service</a>
</div>
</div>
</div>
{% endif %}
{% if article.related_consulting_partner %}
<div class="col-12 col-md-4 mb-3">
<div class="card h-100">
<div class="card-body">
<h5 class="card-title">Partner</h5>
<p class="card-text">{{ article.related_consulting_partner.name }}</p>
<a href="{{ article.related_consulting_partner.get_absolute_url }}" class="btn btn-primary btn-sm">View Partner</a>
</div>
</div>
</div>
{% endif %}
{% if article.related_cloud_provider %}
<div class="col-12 col-md-4 mb-3">
<div class="card h-100">
<div class="card-body">
<h5 class="card-title">Provider</h5>
<p class="card-text">{{ article.related_cloud_provider.name }}</p>
<a href="{{ article.related_cloud_provider.get_absolute_url }}" class="btn btn-primary btn-sm">View Provider</a>
</div>
</div>
</div>
{% endif %}
</div>
</div>
{% endif %}
<!-- Related Articles -->
{% if related_articles %}
<div class="mt-5 pt-4 border-top">
<h3>Related Articles</h3>
<div class="row">
{% for related_article in related_articles %}
<div class="col-12 col-md-4 mb-4">
<div class="card h-100 clickable-card" onclick="cardClicked(event, '{{ related_article.get_absolute_url }}')">
{% if related_article.image %}
<img src="{{ related_article.image.url }}" class="card-img-top" alt="{{ related_article.title }}" style="height: 200px; object-fit: cover;">
{% endif %}
<div class="card-body d-flex flex-column">
<h5 class="card-title">{{ related_article.title }}</h5>
<p class="card-text flex-grow-1">{{ related_article.excerpt|truncatewords:15 }}</p>
<small class="text-muted">{{ related_article.created_at|date:"M d, Y" }}</small>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
<!-- Navigation -->
<div class="mt-5 pt-4 border-top">
<div class="d-flex justify-content-between align-items-center">
<a href="{% url 'services:article_list' %}" class="btn btn-outline-primary">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M19 12H5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12 19L5 12L12 5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
Back to Articles
</a>
<div class="d-flex gap-2">
<button class="btn btn-outline-secondary btn-sm" onclick="window.print()">
<i class="bi bi-printer"></i> Print
</button>
<button class="btn btn-outline-secondary btn-sm" onclick="navigator.share ? navigator.share({title: '{{ article.title }}', url: window.location.href}) : navigator.clipboard.writeText(window.location.href)">
<i class="bi bi-share"></i> Share
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<section class="section bg-primary-subtle">
<div class="section-wrapper container mx-auto px-20 px-lg-0 pt-80 pb-120">
<div class="row">
<div class="col-12 col-lg-4 mb-30 mb-lg-0">
<div class="section-logo mx-auto">
<img class="img-fluid" src="{% static "img/section-logo.png" %}" alt="Sir Vala mascot">
</div>
</div>
<div class="col-12 col-lg-8">
<header class="section-primary__header">
<h2 class="section-h1 fs-40 fs-lg-60">Questions about this article?</h2>
<div class="section-primary__desc">
<p>Have questions or need more information about the topics covered in this article? Get in touch with us!</p>
</div>
<div>
{% embedded_contact_form source="Article Inquiry" %}
</div>
</header>
</div>
</div>
</div>
</section>
{% endblock %}

View file

@ -0,0 +1,217 @@
{% extends 'base.html' %}
{% load static %}
{% load contact_tags %}
{% block title %}Articles{% endblock %}
{% block meta_description %}Explore all articles on Servala, covering cloud services, consulting partners, and cloud provider insights.{% endblock %}
{% block content %}
<section class="section bg-primary-subtle">
<div class="container mx-auto px-20 px-lg-0 pt-40 pb-60">
<header class="section-primary__header text-center">
<h1 class="section-h1 fs-40 fs-lg-64 mb-24">Articles</h1>
<div class="text-gray-300 w-lg-37 mx-auto">
<p class="mb-0">Discover insights, guides, and updates about cloud services, consulting partners, and technology trends.</p>
</div>
</header>
</div>
</section>
<section class="section">
<div class="container-xl mx-auto px-3 px-lg-0 pt-60 pt-lg-80 pb-40">
<div x-data="{ open: window.innerWidth > 1024 }"
@resize.window="open = window.innerWidth > 1024 ? open : true" class="d-lg-flex">
<!-- Filters -->
<div class="w-lg-20 flex-none">
<!-- Mobile Menu -->
<div class="page-action d-lg-none mb-40">
<button @click="open = !open"
class="btn btn-outline-primary btn-md w-100 d-flex justify-content-center align-items-center"
type="button">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none"
xmlns="http://www.w3.org/2000/svg">
<mask id="mask0_115_3182" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0"
y="0" width="24" height="24">
<rect width="24" height="24" fill="#D9D9D9" />
</mask>
<g mask="url(#mask0_115_3182)">
<path d="M7 18V16H17V18H7ZM5 13V11H19V13H5ZM3 8V6H21V8H3Z" fill="#9A63EC" />
</g>
</svg>
<span class="ms-2">Filters</span>
</button>
</div>
<!-- Desktop View -->
<div x-cloak x-show="open || window.innerWidth >= 1024" class="w-lg-85" x-collapse>
<div class="d-flex d-lg-none justify-content-between align-items-center mb-24"
role="button">
<h3 class="sidebar-dropdown__title mb-0">Filters</h3>
<span @click="open = false">
<svg width="24" height="25" viewBox="0 0 24 25" fill="none"
xmlns="http://www.w3.org/2000/svg">
<mask id="mask0_115_3177" style="mask-type:alpha" maskUnits="userSpaceOnUse"
x="0" y="0" width="24" height="25">
<rect y="0.5" width="24" height="24" fill="#D9D9D9" />
</mask>
<g mask="url(#mask0_115_3177)">
<path
d="M6.4 19.5L5 18.1L10.6 12.5L5 6.9L6.4 5.5L12 11.1L17.6 5.5L19 6.9L13.4 12.5L19 18.1L17.6 19.5L12 13.9L6.4 19.5Z"
fill="#160037" />
</g>
</svg>
</span>
</div>
<div class="search-form position-relative mb-24">
<form method="get" x-data="{submitForm() { $refs.filterForm.submit(); } }" x-ref="filterForm">
<!-- Search -->
<div class="mb-24">
<label for="search" class="d-none">Search</label>
<input type="text" id="search" class="input-search" placeholder="Search articles" name="search" value="{{ request.GET.search }}">
<button class="search-button position-absolute top-0 start-0 d-flex justify-content-center align-items-center border-0 bg-transparent p-0"
type="button" title="search">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17.5 17.5L22 22" stroke="#9A63EC" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
<path d="M20 11C20 6.02944 15.9706 2 11 2C6.02944 2 2 6.02944 2 11C2 15.9706 6.02944 20 11 20C15.9706 20 20 15.9706 20 11Z"
stroke="#9A63EC" stroke-width="1.5" stroke-linejoin="round" />
</svg>
</button>
</div>
<!-- Service Filter -->
<div class="pt-24 mb-24">
<div class="d-flex justify-content-between align-items-center h-33 mb-5px" role="button">
<h3 class="sidebar-title mb-0">Service</h3>
</div>
<div>
<select class="form-select" id="service" name="service" @change="submitForm()">
<option value="">All Services</option>
{% for service in available_services %}
<option value="{{ service.id }}" {% if request.GET.service == service.id|stringformat:'i' %}selected{% endif %}>
{{ service.name }}
</option>
{% endfor %}
</select>
</div>
</div>
<!-- Consulting Partners Filter -->
<div class="pt-24 mb-24">
<div class="d-flex justify-content-between align-items-center h-33 mb-5px" role="button">
<h3 class="sidebar-title mb-0">Consulting Partners</h3>
</div>
<div>
<select class="form-select" id="consulting_partner" name="consulting_partner" @change="submitForm()">
<option value="">All Partners</option>
{% for partner in available_consulting_partners %}
<option value="{{ partner.id }}" {% if request.GET.consulting_partner == partner.id|stringformat:'i' %}selected{% endif %}>
{{ partner.name }}
</option>
{% endfor %}
</select>
</div>
</div>
<!-- Cloud Providers Filter -->
<div class="pt-24 mb-24">
<div class="d-flex justify-content-between align-items-center h-33 mb-5px" role="button">
<h3 class="sidebar-title mb-0">Cloud Providers</h3>
</div>
<div>
<select class="form-select" id="cloud_provider" name="cloud_provider" @change="submitForm()">
<option value="">All Providers</option>
{% for provider in available_cloud_providers %}
<option value="{{ provider.id }}" {% if request.GET.cloud_provider == provider.id|stringformat:'i' %}selected{% endif %}>
{{ provider.name }}
</option>
{% endfor %}
</select>
</div>
</div>
<!-- Filter Actions -->
<div class="d-flex gap-2">
<a href="{% url 'services:article_list' %}" class="btn btn-outline-secondary btn-sm">Clear</a>
</div>
</form>
</div>
</div>
</div>
<!-- Article Listing -->
<div class="section__grid flex-1">
<div class="row">
{% for article in articles %}
<div class="col-12 col-md-6 col-lg-4 mb-30">
<div class="card {% if article.is_featured %}card-featured{% endif %} h-100 d-flex flex-column clickable-card"
onclick="cardClicked(event, '{{ article.get_absolute_url }}')">
{% if article.image or article.is_featured %}
<div class="d-flex justify-content-between mb-3">
{% if article.image %}
<div class="card__image flex-shrink-0">
<img src="{{ article.image.url }}" alt="{{ article.title }}" class="img-fluid">
</div>
{% endif %}
{% if article.is_featured %}
<div>
<span class="btn btn-primary btn-sm">Featured</span>
</div>
{% endif %}
</div>
{% endif %}
<div class="card__content d-flex flex-column flex-grow-1">
<div class="card__header">
<h3 class="card__title">
{{ article.title }}
</h3>
<p class="card__subtitle">
<span class="text-muted">
By {{ article.author.get_full_name|default:article.author.username }}
</span>
<span class="text-muted ms-2">
{{ article.created_at|date:"M d, Y" }}
</span>
</p>
</div>
<div class="card__desc flex-grow-1">
<p class="mb-0">{{ article.excerpt|truncatewords:20 }}</p>
</div>
</div>
</div>
</div>
{% empty %}
<div class="col-12">
<div class="alert alert-info">
No articles found matching your criteria.
</div>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
</section>
<section class="section bg-primary-subtle">
<div class="section-wrapper container mx-auto px-20 px-lg-0 pt-80 pb-120">
<div class="row">
<div class="col-12 col-lg-4 mb-30 mb-lg-0">
<div class="section-logo mx-auto">
<img class="img-fluid" src="{% static "img/section-logo.png" %}" alt="Sir Vala mascot">
</div>
</div>
<div class="col-12 col-lg-8">
<header class="section-primary__header">
<h2 class="section-h1 fs-40 fs-lg-60">Looking for specific content?</h2>
<div class="section-primary__desc">
<p>Can't find what you're looking for? Let us know what topics you'd like to see covered!</p>
</div>
<div>
{% embedded_contact_form source="Article Request" %}
</div>
</header>
</div>
</div>
</div>
</section>
{% endblock %}