diff --git a/.gitignore b/.gitignore index 85f96b4..37218ad 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ wheels/ # Project specifics .env hub/db.sqlite3 +hub/media/ \ No newline at end of file diff --git a/hub/media/cloud_provider_logos/cloudscale.png b/hub/media_blubb/cloud_provider_logos/cloudscale.png similarity index 100% rename from hub/media/cloud_provider_logos/cloudscale.png rename to hub/media_blubb/cloud_provider_logos/cloudscale.png diff --git a/hub/media_blubb/cloud_provider_logos/cloudscale_oZ4GYnx.png b/hub/media_blubb/cloud_provider_logos/cloudscale_oZ4GYnx.png new file mode 100644 index 0000000..04c0658 Binary files /dev/null and b/hub/media_blubb/cloud_provider_logos/cloudscale_oZ4GYnx.png differ diff --git a/hub/media/cloud_provider_logos/exoscale-RGB-logo-fullcolor-whiteBG.jpg b/hub/media_blubb/cloud_provider_logos/exoscale-RGB-logo-fullcolor-whiteBG.jpg similarity index 100% rename from hub/media/cloud_provider_logos/exoscale-RGB-logo-fullcolor-whiteBG.jpg rename to hub/media_blubb/cloud_provider_logos/exoscale-RGB-logo-fullcolor-whiteBG.jpg diff --git a/hub/media/service_logos/postgresql.png b/hub/media_blubb/service_logos/postgresql.png similarity index 100% rename from hub/media/service_logos/postgresql.png rename to hub/media_blubb/service_logos/postgresql.png diff --git a/hub/media/service_logos/postgresql_Nq7hKyN.png b/hub/media_blubb/service_logos/postgresql_Nq7hKyN.png similarity index 100% rename from hub/media/service_logos/postgresql_Nq7hKyN.png rename to hub/media_blubb/service_logos/postgresql_Nq7hKyN.png diff --git a/hub/services/admin.py b/hub/services/admin.py index 4481180..2cebb43 100644 --- a/hub/services/admin.py +++ b/hub/services/admin.py @@ -14,8 +14,9 @@ class CategoryAdmin(admin.ModelAdmin): @admin.register(CloudProvider) class CloudProviderAdmin(admin.ModelAdmin): - list_display = ("name", "logo_preview") + list_display = ("name", "slug", "logo_preview") search_fields = ("name",) + prepopulated_fields = {"slug": ("name",)} def logo_preview(self, obj): if obj.logo: diff --git a/hub/services/migrations/0004_cloudprovider_slug.py b/hub/services/migrations/0004_cloudprovider_slug.py new file mode 100644 index 0000000..c273610 --- /dev/null +++ b/hub/services/migrations/0004_cloudprovider_slug.py @@ -0,0 +1,40 @@ +# Generated by Django 5.1.5 on 2025-01-27 14:49 + +from django.db import migrations, models +from django.utils.text import slugify + + +def generate_provider_slugs(apps, schema_editor): + CloudProvider = apps.get_model("services", "CloudProvider") + for provider in CloudProvider.objects.all(): + provider.slug = slugify(provider.name) + counter = 1 + while CloudProvider.objects.filter(slug=provider.slug).exists(): + provider.slug = f"{slugify(provider.name)}-{counter}" + counter += 1 + provider.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ("services", "0003_category_service_categories"), + ] + + operations = [ + migrations.AddField( + model_name="cloudprovider", + name="slug", + field=models.SlugField(unique=True, null=True), + preserve_default=False, + ), + migrations.RunPython( + generate_provider_slugs, reverse_code=migrations.RunPython.noop + ), + migrations.AlterField( + model_name="cloudprovider", + name="slug", + field=models.SlugField(unique=True), + preserve_default=False, + ), + ] diff --git a/hub/services/models.py b/hub/services/models.py index c6338ae..dc94232 100644 --- a/hub/services/models.py +++ b/hub/services/models.py @@ -46,6 +46,7 @@ class Category(models.Model): class CloudProvider(models.Model): name = models.CharField(max_length=100) + slug = models.SlugField(unique=True) description = ProseEditorField(blank=True) logo = models.ImageField( upload_to="cloud_provider_logos/", @@ -57,6 +58,14 @@ class CloudProvider(models.Model): def __str__(self): return self.name + def save(self, *args, **kwargs): + if not self.slug: + self.slug = slugify(self.name) + super().save(*args, **kwargs) + + def get_absolute_url(self): + return reverse("services:provider_detail", kwargs={"slug": self.slug}) + class Country(models.Model): name = models.CharField(max_length=100) diff --git a/hub/services/templates/services/base.html b/hub/services/templates/services/base.html index 7383a28..0a455a0 100644 --- a/hub/services/templates/services/base.html +++ b/hub/services/templates/services/base.html @@ -11,7 +11,18 @@ @@ -28,10 +39,12 @@ overflow-wrap: break-word; word-wrap: break-word; } + .rich-text-content img { max-width: 100%; height: auto; } + .description-preview img { max-width: 100%; height: auto; diff --git a/hub/services/templates/services/provider_detail.html b/hub/services/templates/services/provider_detail.html new file mode 100644 index 0000000..1ace4cd --- /dev/null +++ b/hub/services/templates/services/provider_detail.html @@ -0,0 +1,62 @@ +{% extends 'services/base.html' %} + +{% block content %} +
+
+
+ {% if provider.logo %} + {{ provider.name }} logo + {% endif %} +
+

{{ provider.name }}

+
+ {{ provider.description|safe }} +
+
+
+ +

Available Services

+
+ {% for service in services %} +
+
+
+
+ {% if service.logo %} + {{ service.name }} logo + {% endif %} +
+
{{ service.name }}
+
+
+
+ {{ service.description|safe|truncatewords_html:30 }} +
+
+ {% for category in service.categories.all %} + {{ category.full_path }} + {% endfor %} +
+

+ + Service Level: {{ service.service_level.name }}
+ Price: ${{ service.price }} +
+

+ View Details +
+
+
+ {% empty %} +
+
+ No services available from this provider yet. +
+
+ {% endfor %} +
+
+
+{% endblock %} \ No newline at end of file diff --git a/hub/services/templates/services/service_detail.html b/hub/services/templates/services/service_detail.html index 6bb0d5d..82ad62f 100644 --- a/hub/services/templates/services/service_detail.html +++ b/hub/services/templates/services/service_detail.html @@ -17,9 +17,16 @@

{{ service.name }}

{% if service.cloud_provider.logo %} - {{ service.cloud_provider.name }} logo + + {{ service.cloud_provider.name }} logo + + {% else %} +
+ {{ service.cloud_provider.name }} +
{% endif %} -
{{ service.cloud_provider.name }}
diff --git a/hub/services/templates/services/service_list.html b/hub/services/templates/services/service_list.html index 3944f3b..d6db25e 100644 --- a/hub/services/templates/services/service_list.html +++ b/hub/services/templates/services/service_list.html @@ -86,18 +86,26 @@
{{ service.name }}
{% if service.cloud_provider.logo %} - {{ service.cloud_provider.name }} logo + + {{ service.cloud_provider.name }} logo + + {% else %} +
+ {{ service.cloud_provider.name }} +
{% endif %} -
{{ service.cloud_provider.name }}
-

{{ service.description|safe|truncatewords:30 }}

{% for category in service.categories.all %} {{ category.full_path }} {% endfor %}
+

{{ service.description|safe|truncatewords:30 }}

+

Service Level: {{ service.service_level.name }}
diff --git a/hub/services/urls.py b/hub/services/urls.py index 6964fe3..88673da 100644 --- a/hub/services/urls.py +++ b/hub/services/urls.py @@ -6,4 +6,5 @@ app_name = "services" urlpatterns = [ path("", views.service_list, name="service_list"), path("service//", views.service_detail, name="service_detail"), + path("provider//", views.provider_detail, name="provider_detail"), ] diff --git a/hub/services/views.py b/hub/services/views.py index 134cae3..efa2df0 100644 --- a/hub/services/views.py +++ b/hub/services/views.py @@ -49,3 +49,13 @@ def service_list(request): def service_detail(request, pk): service = get_object_or_404(Service, pk=pk) return render(request, "services/service_detail.html", {"service": service}) + + +def provider_detail(request, slug): + provider = get_object_or_404(CloudProvider, slug=slug) + services = Service.objects.filter(cloud_provider=provider) + context = { + "provider": provider, + "services": services, + } + return render(request, "services/provider_detail.html", context)