diff --git a/hub/services/admin/__init__.py b/hub/services/admin/__init__.py index fba7be9..e8e3470 100644 --- a/hub/services/admin/__init__.py +++ b/hub/services/admin/__init__.py @@ -8,4 +8,5 @@ from .images import * from .leads import * from .pricing import * from .providers import * +from .widgets import * from .services import * diff --git a/hub/services/admin/articles.py b/hub/services/admin/articles.py index 271bf91..f3ebff8 100644 --- a/hub/services/admin/articles.py +++ b/hub/services/admin/articles.py @@ -7,8 +7,8 @@ from django.utils.html import format_html from django import forms from django.core.exceptions import ValidationError - from ..models import Article +from .widgets import ImageLibraryWidget class ArticleAdminForm(forms.ModelForm): @@ -17,6 +17,9 @@ class ArticleAdminForm(forms.ModelForm): class Meta: model = Article fields = "__all__" + widgets = { + 'image_library': ImageLibraryWidget(), + } def clean_title(self): """Validate title length""" diff --git a/hub/services/admin/providers.py b/hub/services/admin/providers.py index 7924762..fb79c1c 100644 --- a/hub/services/admin/providers.py +++ b/hub/services/admin/providers.py @@ -4,9 +4,33 @@ Admin classes for cloud providers and consulting partners from django.contrib import admin from django.utils.html import format_html +from django import forms from adminsortable2.admin import SortableAdminMixin from ..models import CloudProvider, ConsultingPartner, ServiceOffering +from .widgets import ImageLibraryWidget + + +class CloudProviderAdminForm(forms.ModelForm): + """Custom form for CloudProvider admin with image widget""" + + class Meta: + model = CloudProvider + fields = "__all__" + widgets = { + 'image_library': ImageLibraryWidget(), + } + + +class ConsultingPartnerAdminForm(forms.ModelForm): + """Custom form for ConsultingPartner admin with image widget""" + + class Meta: + model = ConsultingPartner + fields = "__all__" + widgets = { + 'image_library': ImageLibraryWidget(), + } class OfferingInline(admin.StackedInline): @@ -34,6 +58,8 @@ class OfferingInline(admin.StackedInline): class CloudProviderAdmin(SortableAdminMixin, admin.ModelAdmin): """Admin configuration for CloudProvider model""" + form = CloudProviderAdminForm + list_display = ( "name", "slug", @@ -77,6 +103,8 @@ class CloudProviderAdmin(SortableAdminMixin, admin.ModelAdmin): class ConsultingPartnerAdmin(SortableAdminMixin, admin.ModelAdmin): """Admin configuration for ConsultingPartner model""" + form = ConsultingPartnerAdminForm + list_display = ( "name", "website", diff --git a/hub/services/admin/services.py b/hub/services/admin/services.py index 28b3383..9932ff3 100644 --- a/hub/services/admin/services.py +++ b/hub/services/admin/services.py @@ -4,6 +4,7 @@ Admin classes for services and service offerings from django.contrib import admin from django.utils.html import format_html +from django import forms from ..models import ( Service, @@ -13,6 +14,18 @@ from ..models import ( Plan, PlanPrice, ) +from .widgets import ImageLibraryWidget + + +class ServiceAdminForm(forms.ModelForm): + """Custom form for Service admin with image widget""" + + class Meta: + model = Service + fields = "__all__" + widgets = { + 'image_library': ImageLibraryWidget(), + } class ExternalLinkInline(admin.TabularInline): @@ -79,6 +92,8 @@ class OfferingInline(admin.StackedInline): class ServiceAdmin(admin.ModelAdmin): """Admin configuration for Service model""" + form = ServiceAdminForm + list_display = ( "name", "logo_preview", diff --git a/hub/services/admin/widgets.py b/hub/services/admin/widgets.py new file mode 100644 index 0000000..81e1b1e --- /dev/null +++ b/hub/services/admin/widgets.py @@ -0,0 +1,226 @@ +""" +Custom widgets for Django admin interface +""" + +from django import forms +from django.utils.html import format_html +from django.utils.safestring import mark_safe +from django.urls import reverse +from django.conf import settings + +from ..models import ImageLibrary + + +class ImageLibraryWidget(forms.Select): + """Custom widget for selecting images from the library with visual preview""" + + def __init__(self, attrs=None): + super().__init__(attrs) + self.attrs.update( + { + "class": "image-library-select", + "style": "display: none;", # Hide the original select + } + ) + + def render(self, name, value, attrs=None, renderer=None): + """Render the widget with image previews""" + # Get the original select element + original_select = super().render(name, value, attrs, renderer) + + # Get all images from the library + images = ImageLibrary.objects.all().order_by("-uploaded_at") + + # Create the visual interface + html_parts = [ + '
", + self._get_styles(), + self._get_javascript(), + ] + ) + + return mark_safe("".join(html_parts)) + + def _get_styles(self): + """Return CSS styles for the widget""" + return """ + + """ + + def _get_javascript(self): + """Return JavaScript for the widget functionality""" + return """ + + """ + + class Media: + css = { + "all": ( + "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css", + ) + }