Compare commits
6 commits
a54b1b1108
...
3c365c0824
Author | SHA1 | Date | |
---|---|---|---|
3c365c0824 | |||
160734e9d4 | |||
7975d0a00e | |||
73d9153eca | |||
2567c8455c | |||
76f64b83ea |
9 changed files with 108 additions and 44 deletions
|
@ -80,6 +80,13 @@ class Service(ServalaModelMixin, models.Model):
|
|||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
@property
|
||||
def featured_links(self):
|
||||
"""Return external links marked as featured."""
|
||||
if not self.external_links:
|
||||
return []
|
||||
return [link for link in self.external_links if link.get("featured")]
|
||||
|
||||
|
||||
def validate_dict(data, required_fields=None, allow_empty=True):
|
||||
if not data:
|
||||
|
|
|
@ -1,21 +1,22 @@
|
|||
{% extends "frontend/base.html" %}
|
||||
{% load static i18n %}
|
||||
{% load allauth account socialaccount %}
|
||||
|
||||
{% block html_title %}
|
||||
{% translate "Sign in" %}
|
||||
{% endblock html_title %}
|
||||
|
||||
{% block page_title %}
|
||||
{% translate "Welcome to Servala" %}
|
||||
{% endblock page_title %}
|
||||
|
||||
{% block card_header %}
|
||||
<div class="card-header text-center py-4" style="background: linear-gradient(135deg, var(--bs-primary), #8B5CF6); border-radius: 0.5rem 0.5rem 0 0;">
|
||||
<img src="{% static 'img/Servala-4.png' %}" alt="Servala" class="mb-3" style="height: 70px;">
|
||||
<div class="card-header text-center py-4"
|
||||
style="background: linear-gradient(135deg, var(--bs-primary), #8B5CF6);
|
||||
border-radius: 0.5rem 0.5rem 0 0">
|
||||
<img src="{% static 'img/Servala-4.png' %}"
|
||||
alt="Servala"
|
||||
class="mb-3"
|
||||
style="height: 70px">
|
||||
</div>
|
||||
{% endblock card_header %}
|
||||
|
||||
{% block card_content %}
|
||||
<!-- Main Sign In Section -->
|
||||
{% if SOCIALACCOUNT_ENABLED %}
|
||||
|
@ -24,9 +25,10 @@
|
|||
<div class="mb-4">
|
||||
<div class="text-center mb-4">
|
||||
<h5 class="text-primary mb-2">{% translate "Ready to get started?" %}</h5>
|
||||
<p class="text-muted mb-0">{% translate "Sign in to access your managed service instances and the Servala service catalog" %}</p>
|
||||
<p class="text-muted mb-0">
|
||||
{% translate "Sign in to access your managed service instances and the Servala service catalog" %}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{% for provider in socialaccount_providers %}
|
||||
{% provider_login_url provider process=process scope=scope auth_params=auth_params as href %}
|
||||
<form method="post" action="{{ href }}">
|
||||
|
@ -35,7 +37,9 @@
|
|||
<button type="submit"
|
||||
class="btn btn-primary btn-lg w-100 py-3 mb-4 fw-semibold"
|
||||
title="{{ provider.name }}"
|
||||
style="border-radius: 12px; box-shadow: 0 4px 15px rgba(154, 99, 236, 0.2); background: linear-gradient(135deg, var(--bs-primary), #8B5CF6);">
|
||||
style="border-radius: 12px;
|
||||
box-shadow: 0 4px 15px rgba(154, 99, 236, 0.2);
|
||||
background: linear-gradient(135deg, var(--bs-primary), #8B5CF6)">
|
||||
<span>{% translate "Sign in with VSHN Account" %}</span>
|
||||
</button>
|
||||
</form>
|
||||
|
@ -43,7 +47,6 @@
|
|||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
<!-- Feature Preview & Learn More Section -->
|
||||
<div class="mt-4 pt-3 border-top">
|
||||
<div class="row g-3 text-center">
|
||||
|
@ -72,8 +75,8 @@
|
|||
<i class="bi bi-info-circle" style="font-size: 1.2rem;"></i>
|
||||
</div>
|
||||
<small class="text-muted fw-medium">
|
||||
<a href="https://servala.com"
|
||||
target="_blank"
|
||||
<a href="https://servala.com"
|
||||
target="_blank"
|
||||
class="text-decoration-none text-muted">
|
||||
{% translate "Learn more" %}
|
||||
<i class="bi bi-arrow-up-right ms-1" style="font-size: 0.7rem;"></i>
|
||||
|
@ -82,7 +85,6 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Alternative Login Options (Admin) -->
|
||||
<div class="mt-4 pt-3 border-top text-center">
|
||||
<small class="text-muted">
|
||||
|
@ -96,7 +98,6 @@
|
|||
</a>
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="collapse mt-3" id="login-form">
|
||||
<div class="card bg-light border-0 shadow-sm" style="border-radius: 12px;">
|
||||
<div class="card-body p-4">
|
||||
|
|
|
@ -5,13 +5,18 @@
|
|||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="{% static 'mazer/compiled/css/app.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'mazer/compiled/css/app-dark.css' %}">
|
||||
<link rel="stylesheet"
|
||||
href="{% static 'mazer/compiled/css/app-dark.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'mazer/compiled/css/iconly.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'css/servala.css' %}">
|
||||
<link rel="icon" type="image/x-icon" href="{% static 'img/favicon.ico' %}">
|
||||
<script src="{% static "js/htmx.min.js" %}" defer></script>
|
||||
</head>
|
||||
<title>{% block html_title %}Dashboard{% endblock html_title %} – Servala</title>
|
||||
<title>
|
||||
{% block html_title %}
|
||||
Dashboard
|
||||
{% endblock html_title %}
|
||||
– Servala</title>
|
||||
</head>
|
||||
<body hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'>
|
||||
<script src="{% static 'mazer/static/js/initTheme.js' %}"></script>
|
||||
|
|
|
@ -26,6 +26,24 @@
|
|||
<div class="row">
|
||||
<p>{{ service.description|default:"No description available."|urlize }}</p>
|
||||
</div>
|
||||
{% if service.external_links %}
|
||||
<div class="row mt-3">
|
||||
<div class="col-12">
|
||||
<h6 class="mb-3">{% translate "External Links" %}</h6>
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
{% for link in service.external_links %}
|
||||
<a href="{{ link.url }}"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="btn btn-outline-primary btn-sm">
|
||||
{{ link.title }}
|
||||
<i class="bi bi-box-arrow-up-right ms-1"></i>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
|
|
|
@ -16,10 +16,10 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="row service-cards-container">
|
||||
{% for service in services %}
|
||||
<div class="col-6 col-lg-3 col-md-4">
|
||||
<div class="card">
|
||||
<div class="card h-100 d-flex flex-column">
|
||||
<div class="card-header d-flex align-items-center">
|
||||
{% if service.logo %}
|
||||
<img src="{{ service.logo.url }}"
|
||||
|
@ -33,11 +33,23 @@
|
|||
<small class="text-muted">{{ service.category }}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="card-body flex-grow-1">
|
||||
{% if service.description %}<p class="card-text">{{ service.description|urlize }}</p>{% endif %}
|
||||
</div>
|
||||
<div class="card-footer d-flex justify-content-between">
|
||||
<span></span>
|
||||
<div class="card-footer d-flex justify-content-between align-items-center gap-2">
|
||||
{% if service.featured_links %}
|
||||
{% with featured_link=service.featured_links.0 %}
|
||||
<a href="{{ featured_link.url }}"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="btn btn-outline-primary">
|
||||
{{ featured_link.title }}
|
||||
<i class="bi bi-box-arrow-up-right ms-1"></i>
|
||||
</a>
|
||||
{% endwith %}
|
||||
{% else %}
|
||||
<span></span>
|
||||
{% endif %}
|
||||
<a href="{{ service.slug }}/" class="btn btn-light-primary">{% translate "View Availability" %}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -116,7 +116,8 @@
|
|||
{% endblocktranslate %}
|
||||
</p>
|
||||
<div>
|
||||
<a href="{{ account_href }}" target="_blank"
|
||||
<a href="{{ account_href }}"
|
||||
target="_blank"
|
||||
class="btn btn-primary btn-lg icon icon-left btn-keycloak">
|
||||
<span class="mx-1">{% translate "VSHN Account Console" %}</span>
|
||||
</a>
|
||||
|
|
|
@ -1,28 +1,29 @@
|
|||
<div class="alert alert-{{ message.tags }} alert-dismissible" id="auto-dismiss-alert-{{ forloop.counter0|default:'0' }}">
|
||||
<div class="alert alert-{{ message.tags }} alert-dismissible"
|
||||
id="auto-dismiss-alert-{{ forloop.counter0|default:'0' }}">
|
||||
{{ message }}
|
||||
<button type="button"
|
||||
class="btn-close"
|
||||
data-bs-dismiss="alert"
|
||||
aria-label="Close"></button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const alert = document.getElementById('auto-dismiss-alert-{{ forloop.counter0|default:'0' }}');
|
||||
if (alert) {
|
||||
setTimeout(function() {
|
||||
let opacity = 1;
|
||||
const fadeOutInterval = setInterval(function() {
|
||||
if (opacity > 0.05) {
|
||||
opacity -= 0.05;
|
||||
alert.style.opacity = opacity;
|
||||
} else {
|
||||
clearInterval(fadeOutInterval);
|
||||
const bsAlert = new bootstrap.Alert(alert);
|
||||
bsAlert.close();
|
||||
}
|
||||
}, 25);
|
||||
}, 5000);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const alert = document.getElementById('auto-dismiss-alert-{{ forloop.counter0|default:'
|
||||
0 ' }}');
|
||||
if (alert) {
|
||||
setTimeout(function() {
|
||||
let opacity = 1;
|
||||
const fadeOutInterval = setInterval(function() {
|
||||
if (opacity > 0.05) {
|
||||
opacity -= 0.05;
|
||||
alert.style.opacity = opacity;
|
||||
} else {
|
||||
clearInterval(fadeOutInterval);
|
||||
const bsAlert = new bootstrap.Alert(alert);
|
||||
bsAlert.close();
|
||||
}
|
||||
}, 25);
|
||||
}, 5000);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -51,7 +51,8 @@ class SupportView(OrganizationViewMixin, FormView):
|
|||
self.request,
|
||||
mark_safe(
|
||||
_(
|
||||
'There was an error submitting your support request. Please try again or contact us directly at <a href="mailto:servala-support@vshn.ch">servala-support@vshn.ch</a>.'
|
||||
"There was an error submitting your support request. "
|
||||
"Please try again or contact us directly at <a href=\"mailto:servala-support@vshn.ch\">servala-support@vshn.ch</a>."
|
||||
)
|
||||
),
|
||||
)
|
||||
|
|
|
@ -174,3 +174,21 @@ a.btn-keycloak {
|
|||
margin-top: -16px;
|
||||
padding-right: 28px;
|
||||
}
|
||||
|
||||
/* Service cards equal height styling */
|
||||
.service-cards-container .card {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.service-cards-container .card-body {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.service-cards-container .card-footer {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue