diff --git a/backend/igny8_core/admin/site.py b/backend/igny8_core/admin/site.py index 915d4450..62e3f60f 100644 --- a/backend/igny8_core/admin/site.py +++ b/backend/igny8_core/admin/site.py @@ -45,6 +45,8 @@ class Igny8AdminSite(admin.AdminSite): ('igny8_core_auth', 'User'), ('igny8_core_auth', 'SiteUserAccess'), ('igny8_core_auth', 'PasswordResetToken'), + ('site_building', 'SiteBlueprint'), + ('site_building', 'PageBlueprint'), ], }, 'Global Reference Data': { @@ -52,6 +54,10 @@ class Igny8AdminSite(admin.AdminSite): ('igny8_core_auth', 'Industry'), ('igny8_core_auth', 'IndustrySector'), ('igny8_core_auth', 'SeedKeyword'), + ('site_building', 'BusinessType'), + ('site_building', 'AudienceProfile'), + ('site_building', 'BrandPersonality'), + ('site_building', 'HeroImageryDirection'), ], }, 'Planner': { diff --git a/backend/igny8_core/business/site_building/admin.py b/backend/igny8_core/business/site_building/admin.py new file mode 100644 index 00000000..9765f0be --- /dev/null +++ b/backend/igny8_core/business/site_building/admin.py @@ -0,0 +1,250 @@ +""" +Admin interface for Site Builder models +""" +from django.contrib import admin +from django.utils.html import format_html +from django.urls import reverse +from igny8_core.admin.base import SiteSectorAdminMixin +from .models import ( + SiteBlueprint, + PageBlueprint, + BusinessType, + AudienceProfile, + BrandPersonality, + HeroImageryDirection, +) + + +class PageBlueprintInline(admin.TabularInline): + """Inline admin for Page Blueprints within Site Blueprint""" + model = PageBlueprint + extra = 0 + fields = ['slug', 'title', 'type', 'status', 'order'] + readonly_fields = ['slug', 'title', 'type', 'status', 'order'] + can_delete = False + show_change_link = True + + +@admin.register(SiteBlueprint) +class SiteBlueprintAdmin(SiteSectorAdminMixin, admin.ModelAdmin): + """Admin interface for Site Blueprints""" + list_display = [ + 'name', + 'get_site_display', + 'get_sector_display', + 'status', + 'hosting_type', + 'version', + 'get_pages_count', + 'created_at', + ] + list_filter = ['status', 'hosting_type', 'site', 'sector', 'account', 'created_at'] + search_fields = ['name', 'description', 'site__name', 'sector__name'] + readonly_fields = ['created_at', 'updated_at', 'version', 'deployed_version'] + ordering = ['-created_at'] + inlines = [PageBlueprintInline] + + fieldsets = ( + ('Basic Information', { + 'fields': ('name', 'description', 'account', 'site', 'sector') + }), + ('Status & Configuration', { + 'fields': ('status', 'hosting_type', 'version', 'deployed_version') + }), + ('Configuration Data', { + 'fields': ('config_json',), + 'classes': ('collapse',), + }), + ('Structure Data', { + 'fields': ('structure_json',), + 'classes': ('collapse',), + }), + ('Timestamps', { + 'fields': ('created_at', 'updated_at'), + 'classes': ('collapse',), + }), + ) + + def get_site_display(self, obj): + """Safely get site name""" + try: + if obj.site: + url = reverse('admin:igny8_core_auth_site_change', args=[obj.site.id]) + return format_html('{}', url, obj.site.name) + return '-' + except: + return '-' + get_site_display.short_description = 'Site' + + def get_sector_display(self, obj): + """Safely get sector name""" + try: + return obj.sector.name if obj.sector else '-' + except: + return '-' + get_sector_display.short_description = 'Sector' + + def get_pages_count(self, obj): + """Get count of pages for this blueprint""" + try: + count = obj.pages.count() + if count > 0: + url = reverse('admin:site_building_pageblueprint_changelist') + return format_html('{} pages', url, obj.id, count) + return '0 pages' + except: + return '0 pages' + get_pages_count.short_description = 'Pages' + + +@admin.register(PageBlueprint) +class PageBlueprintAdmin(SiteSectorAdminMixin, admin.ModelAdmin): + """Admin interface for Page Blueprints""" + list_display = [ + 'title', + 'slug', + 'get_site_blueprint_display', + 'type', + 'status', + 'order', + 'get_site_display', + 'get_sector_display', + 'created_at', + ] + list_filter = ['status', 'type', 'site_blueprint', 'site', 'sector', 'created_at'] + search_fields = ['title', 'slug', 'site_blueprint__name', 'site__name'] + readonly_fields = ['created_at', 'updated_at'] + ordering = ['site_blueprint', 'order', 'created_at'] + + fieldsets = ( + ('Basic Information', { + 'fields': ('site_blueprint', 'title', 'slug', 'type', 'status', 'order') + }), + ('Account & Site', { + 'fields': ('account', 'site', 'sector'), + 'classes': ('collapse',), + }), + ('Content Blocks', { + 'fields': ('blocks_json',), + 'classes': ('collapse',), + }), + ('Timestamps', { + 'fields': ('created_at', 'updated_at'), + 'classes': ('collapse',), + }), + ) + + def get_site_blueprint_display(self, obj): + """Safely get site blueprint name with link""" + try: + if obj.site_blueprint: + url = reverse('admin:site_building_siteblueprint_change', args=[obj.site_blueprint.id]) + return format_html('{}', url, obj.site_blueprint.name) + return '-' + except: + return '-' + get_site_blueprint_display.short_description = 'Site Blueprint' + + def get_site_display(self, obj): + """Safely get site name""" + try: + if obj.site: + url = reverse('admin:igny8_core_auth_site_change', args=[obj.site.id]) + return format_html('{}', url, obj.site.name) + return '-' + except: + return '-' + get_site_display.short_description = 'Site' + + def get_sector_display(self, obj): + """Safely get sector name""" + try: + return obj.sector.name if obj.sector else '-' + except: + return '-' + get_sector_display.short_description = 'Sector' + + +@admin.register(BusinessType) +class BusinessTypeAdmin(admin.ModelAdmin): + """Admin interface for Business Types""" + list_display = ['name', 'description', 'is_active', 'order', 'created_at'] + list_filter = ['is_active', 'created_at'] + search_fields = ['name', 'description'] + list_editable = ['is_active', 'order'] + ordering = ['order', 'name'] + + fieldsets = ( + ('Basic Information', { + 'fields': ('name', 'description', 'is_active', 'order') + }), + ('Timestamps', { + 'fields': ('created_at', 'updated_at'), + 'classes': ('collapse',), + }), + ) + readonly_fields = ['created_at', 'updated_at'] + + +@admin.register(AudienceProfile) +class AudienceProfileAdmin(admin.ModelAdmin): + """Admin interface for Audience Profiles""" + list_display = ['name', 'description', 'is_active', 'order', 'created_at'] + list_filter = ['is_active', 'created_at'] + search_fields = ['name', 'description'] + list_editable = ['is_active', 'order'] + ordering = ['order', 'name'] + + fieldsets = ( + ('Basic Information', { + 'fields': ('name', 'description', 'is_active', 'order') + }), + ('Timestamps', { + 'fields': ('created_at', 'updated_at'), + 'classes': ('collapse',), + }), + ) + readonly_fields = ['created_at', 'updated_at'] + + +@admin.register(BrandPersonality) +class BrandPersonalityAdmin(admin.ModelAdmin): + """Admin interface for Brand Personalities""" + list_display = ['name', 'description', 'is_active', 'order', 'created_at'] + list_filter = ['is_active', 'created_at'] + search_fields = ['name', 'description'] + list_editable = ['is_active', 'order'] + ordering = ['order', 'name'] + + fieldsets = ( + ('Basic Information', { + 'fields': ('name', 'description', 'is_active', 'order') + }), + ('Timestamps', { + 'fields': ('created_at', 'updated_at'), + 'classes': ('collapse',), + }), + ) + readonly_fields = ['created_at', 'updated_at'] + + +@admin.register(HeroImageryDirection) +class HeroImageryDirectionAdmin(admin.ModelAdmin): + """Admin interface for Hero Imagery Directions""" + list_display = ['name', 'description', 'is_active', 'order', 'created_at'] + list_filter = ['is_active', 'created_at'] + search_fields = ['name', 'description'] + list_editable = ['is_active', 'order'] + ordering = ['order', 'name'] + + fieldsets = ( + ('Basic Information', { + 'fields': ('name', 'description', 'is_active', 'order') + }), + ('Timestamps', { + 'fields': ('created_at', 'updated_at'), + 'classes': ('collapse',), + }), + ) + readonly_fields = ['created_at', 'updated_at'] + diff --git a/backend/igny8_core/business/site_building/apps.py b/backend/igny8_core/business/site_building/apps.py index 51b45348..b5208f06 100644 --- a/backend/igny8_core/business/site_building/apps.py +++ b/backend/igny8_core/business/site_building/apps.py @@ -5,5 +5,12 @@ class SiteBuildingConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'igny8_core.business.site_building' verbose_name = 'Site Building' + + def ready(self): + """Import admin to register models""" + try: + import igny8_core.business.site_building.admin # noqa + except ImportError: + pass diff --git a/frontend/src/pages/Sites/Builder/Blueprints.tsx b/frontend/src/pages/Sites/Builder/Blueprints.tsx index ecb40de7..8ad95dba 100644 --- a/frontend/src/pages/Sites/Builder/Blueprints.tsx +++ b/frontend/src/pages/Sites/Builder/Blueprints.tsx @@ -231,8 +231,7 @@ export default function SiteBuilderBlueprints() { {blueprint.description}

)} - -
+
Status {blueprint.status}