49 KiB
Django Admin Improvement Plan - Unfold Edition
Version: 2.0.0
Created: December 14, 2025
Status: Implementation Phase
Priority: 🔴 High - 3-4 weeks implementation
Dependencies: After Plan Management Implementation
Theme: Django Unfold (Modern Admin Theme)
🔧 CRITICAL FIXES APPLIED (December 14, 2025)
Problem: Multiple Conflicting Admin Systems
The system had 3 conflicting admin systems running simultaneously:
- Default Django admin
- Custom IGNY8 admin modifications
- Unfold theme (partially installed)
This caused style conflicts, crashes, and inconsistent UI.
Solution: Clean Unfold-Only Installation
✅ Fixed Issues:
-
Backend Container Crashing - Missing
unfoldmodule- Added
django-unfold==0.73.1to requirements.txt - Rebuilt Docker image:
igny8-backend:latest - All containers now use new image (backend, celery_worker, celery_beat, flower)
- Added
-
Admin Apps Configuration Error -
AttributeError: module 'django.contrib.admin' has no attribute 'apps'- Fixed import in
admin/apps.py:from django.contrib.admin.apps import AdminConfig - Import Unfold AFTER apps are ready in
ready()method
- Fixed import in
-
Admin Site Inheritance - Mixed admin systems
- Changed
Igny8AdminSitefromadmin.AdminSite→UnfoldAdminSite - All admin classes now inherit from Unfold's
ModelAdmin
- Changed
-
Celery Admin Filters - Using wrong filter classes
- Changed from
DateRangeFilter→RangeDateFilter(Unfold version) - Updated
CeleryTaskResultAdminto useModelAdminfrom Unfold
- Changed from
-
Middleware Configuration
- Added
simple_history.middleware.HistoryRequestMiddleware
- Added
-
INSTALLED_APPS Order
- Unfold apps MUST be before
django.contrib.admin - Configured properly in settings.py
- Unfold apps MUST be before
-
UserAdmin Popup Styling - User edit form in popup used default Django styling
- Changed
UserAdminto inherit from bothBaseUserAdminandModelAdmin - Multiple inheritance preserves Django user functionality + Unfold styling
- Popups now use Unfold templates with modern icons and widgets
- Changed
✅ Result: Single Clean Admin System
- ONLY Unfold - No more conflicts
- Modern, responsive UI with Tailwind CSS
- Dark mode support
- Advanced filters and bulk operations built-in
- All popups and forms use Unfold styling
- All containers running healthy
Executive Summary
This document outlines a comprehensive improvement plan for the IGNY8 Django Admin interface using Django Unfold, a modern, feature-rich admin theme. Unfold provides a beautiful UI based on Tailwind CSS, advanced filtering, bulk operations, dark mode, and extensive customization options - eliminating the need for custom CSS/JS while providing enterprise-grade functionality out of the box.
Key Objectives:
- ✅ COMPLETED: Install and configure Unfold theme
- Leverage Unfold's built-in features for UI/UX excellence
- Reorganize sidebar menu using Unfold's navigation system
- Remove unused/empty models
- Implement bulk operations with Unfold's action system
- Add Celery task monitoring with Unfold integration
- Create operational dashboards using Unfold's dashboard tools
- Implement advanced filtering with Unfold's filter extensions
What Makes This Different:
- Modern UI Out-of-the-Box: No custom CSS needed - Unfold provides beautiful, responsive design
- Built-in Features: Bulk operations, advanced filters, charts, dark mode included
- Django Integration: Works seamlessly with django-import-export, django-simple-history, django-celery-results
- Extensible: Easy to customize with Unfold's configuration system
Current State Analysis
✅ Completed (Phase 0) - December 14, 2025
- ✅ Unfold Installation - django-unfold==0.73.1 installed in requirements.txt
- ✅ Docker Image Rebuilt - igny8-backend:latest rebuilt with all dependencies
- ✅ Settings Configuration - UNFOLD settings configured in settings.py
- ✅ Admin Site Update - Igny8AdminSite now inherits from UnfoldAdminSite
- ✅ Admin Apps Fixed - Igny8AdminConfig properly imports Unfold after apps ready
- ✅ Celery Admin Updated - CeleryTaskResultAdmin uses Unfold ModelAdmin & filters
- ✅ Supporting Packages - django-simple-history, django-import-export, django-celery-results installed
- ✅ Middleware Updated - simple_history.middleware.HistoryRequestMiddleware added
- ✅ Static Files - Unfold assets collected via collectstatic
- ✅ All Containers Running - Backend, Celery Worker, Celery Beat, Flower all healthy
- ✅ Conflicts Resolved - No more mixed admin systems (default + custom + Unfold)
- ✅ Single Admin System - Unfold ONLY - clean, modern, no conflicts
✅ Strengths
- Modern UI Theme - Unfold provides beautiful, responsive Tailwind-based design with zero custom CSS
- Consistent Styling - All admin pages, popups, and forms use Unfold templates
- Custom Admin Site - Igny8AdminSite with logical grouping (maintained)
- Multi-Tenancy Support - AccountAdminMixin and SiteSectorAdminMixin
- Payment Approval Workflow - Comprehensive payment approval system
- Custom Actions - API key generation, payment approval/rejection
- Field Customization - Custom fieldsets and readonly fields
⚠️ Issues Remaining
1. Sidebar Menu Organization
- ✅ Current get_app_list() structure works
- Missing PlanLimitUsage model (needs to be added to admin)
- Some empty groups appearing (site_building models don't exist)
- Need to clean up model groupings
2. Unused/Empty Models (Same as before)
- site_building models referenced but don't exist
- Duplicate model registrations need cleanup
3. Missing Features (Now easier with Unfold)
- CSV/Excel export - Can use Unfold's import_export integration
- Bulk operations - Use Unfold's enhanced action system
- Celery monitoring - Use Unfold's contrib package for better UI
- Admin dashboard - Use Unfold's dashboard widgets
- Advanced filtering - Use Unfold's filter contrib package
Unfold Features Available
Built-in Features We'll Use:
-
Visual Interface
- Modern Tailwind CSS-based design
- Dark mode support (automatic)
- Responsive layout (mobile-friendly)
- Beautiful forms and tables
-
Advanced Filtering
unfold.contrib.filters- Enhanced filter UI- Date range filters with calendar
- Autocomplete filters for foreign keys
- Numeric range filters
-
Import/Export
unfold.contrib.import_export- Styled import/export UI- Works seamlessly with django-import-export
- Beautiful file upload/download interface
-
History/Audit Trail
unfold.contrib.simple_history- Enhanced history UI- Timeline view of changes
- User attribution and timestamps
-
Actions
- Enhanced bulk actions UI
- Custom action styling
- Progress indicators
-
Dashboard Components
- Cards for metrics
- Charts (Chart.js integration)
- Custom widgets
- Activity feeds
-
Other Features
- Inline tabs for grouping
- Conditional field visibility
- WYSIWYG editor (Trix)
- Sortable inlines
- Command palette (Cmd+K search)
Phase 1: Critical Fixes & Model Updates (Week 1)
1.1 Clean Up Sidebar Menu Organization
Problem: site_building models referenced but don't exist, causing confusion. Some empty groups appearing.
Action:
-
Remove non-existent site_building models from
backend/igny8_core/admin/site.pycustom_groups:- BusinessType
- AudienceProfile
- BrandPersonality
- HeroImageryDirection
-
Add missing PlanLimitUsage model to Billing group:
'Billing & Accounts': { 'models': [ ('igny8_core_auth', 'Plan'), ('billing', 'PlanLimitUsage'), # ADD THIS ('igny8_core_auth', 'Account'), # ... rest of models ], }, -
Verify all referenced models exist - check that each model in custom_groups is actually registered
Files to modify:
/data/app/igny8/backend/igny8_core/admin/site.py- Verify PlanLimitUsage is registered in admin
1.2 Resolve Duplicate Model Registrations
Problem: Multiple models registered twice causing conflicts
Action:
- Keep only
modules/billing/admin.pyversions (they have full workflow) - Remove or comment out duplicates in
business/billing/admin.py:- Invoice
- Payment
- CreditPackage
- CreditCostConfig
Files to modify:
/data/app/igny8/backend/igny8_core/business/billing/admin.py
1.3 Update Admin Classes to Use Unfold
Current State: Most admin classes inherit from admin.ModelAdmin
Action: Update all admin classes to inherit from Unfold's ModelAdmin:
from unfold.admin import ModelAdmin
from unfold.contrib.filters.admin import RangeDateFilter
from unfold.contrib.import_export.forms import ExportForm, ImportForm
class TasksAdmin(SiteSectorAdminMixin, ModelAdmin):
# Unfold-specific features
compressed_fields = True # Compact form layout
warn_unsaved_form = True # Warn before leaving unsaved form
# Standard Django admin
list_display = ['title', 'status', 'cluster', 'created_at']
list_filter = [
('created_at', RangeDateFilter), # Unfold date range filter
'status',
]
search_fields = ['title', 'description']
actions_detail = ['generate_content', 'assign_cluster'] # Actions in detail view
Key Files to Update:
/data/app/igny8/backend/igny8_core/modules/writer/admin.py/data/app/igny8/backend/igny8_core/modules/planner/admin.py/data/app/igny8/backend/igny8_core/modules/billing/admin.py/data/app/igny8/backend/igny8_core/business/automation/admin.py/data/app/igny8/backend/igny8_core/business/integration/admin.py- All other admin files
1.4 Test Admin Configuration
Action: After cleanup, verify:
- All sidebar groups display correctly
- No 404 errors when clicking model links
- All registered models appear in appropriate groups
- No empty groups in sidebar
Phase 2: Bulk Operations & Export (Week 2)
2.1 Implement CSV/Excel Export with Unfold
Install Already Complete: django-import-export and unfold.contrib.import_export
Action: Add ExportMixin and ImportMixin to admin classes:
from import_export.admin import ImportExportModelAdmin
from unfold.contrib.import_export.forms import ExportForm, ImportForm, SelectableFieldsExportForm
class TasksAdmin(SiteSectorAdminMixin, ImportExportModelAdmin, ModelAdmin):
import_form_class = ImportForm
export_form_class = ExportForm
# Define what fields to export
class TaskResource(resources.ModelResource):
class Meta:
model = Tasks
fields = ('id', 'title', 'status', 'cluster__name', 'site__name', 'created_at')
export_order = fields
resource_class = TaskResource
Priority Models for Export:
- ✅ Tasks
- ✅ Content
- ✅ Keywords
- ✅ Payments
- ✅ CreditTransactions
- ✅ Clusters
2.2 Add Bulk Operations with Unfold Actions
Unfold provides enhanced action UI automatically
Action: Add actions to admin classes:
from django.contrib import admin
from unfold.decorators import action
class TasksAdmin(SiteSectorAdminMixin, ModelAdmin):
actions = ['bulk_set_in_progress', 'bulk_set_completed', 'bulk_assign_cluster']
@action(description="Mark as In Progress")
def bulk_set_in_progress(self, request, queryset):
updated = queryset.update(status='in_progress')
self.message_user(request, f'{updated} tasks marked as in progress', 'SUCCESS')
@action(description="Mark as Completed")
def bulk_set_completed(self, request, queryset):
updated = queryset.update(status='completed')
self.message_user(request, f'{updated} tasks completed', 'SUCCESS')
@action(description="Assign to Cluster", form_class=BulkAssignClusterForm)
def bulk_assign_cluster(self, request, queryset):
# Unfold will show form with cluster selection
if 'cluster' in request.POST:
cluster_id = request.POST['cluster']
queryset.update(cluster_id=cluster_id)
self.message_user(request, f'Assigned to cluster', 'SUCCESS')
Bulk Actions to Add:
Tasks Admin
- Mark as Draft
- Mark as In Progress
- Mark as Completed
- Assign to Cluster
- Generate Content (trigger AI)
Content Admin
- Publish to WordPress
- Change Status
- Add Taxonomy
- Update SEO Settings
Keywords Admin
- Assign to Cluster
- Set Priority
- Mark for Research
Payments Admin
- Approve Payments (already exists)
- Reject Payments (already exists)
- Export Transactions (add)
2.3 Advanced Filtering with Unfold
Unfold provides beautiful filter UI out of the box
Action: Use Unfold's filter contrib for enhanced filtering:
from unfold.contrib.filters.admin import (
RangeDateFilter,
RangeDateTimeFilter,
RangeNumericFilter,
SingleNumericFilter,
SliderNumericFilter,
)
from unfold.contrib.filters.admin import RelatedDropdownFilter, ChoicesDropdownFilter
class CreditTransactionAdmin(AccountAdminMixin, ModelAdmin):
list_filter = [
('created_at', RangeDateFilter), # Beautiful date range picker
('amount', RangeNumericFilter), # Numeric range with slider
('account', RelatedDropdownFilter), # Dropdown with search
('transaction_type', ChoicesDropdownFilter), # Enhanced dropdown
]
Filter Types to Implement:
| Model | Filters |
|---|---|
| CreditTransaction | Date range, Amount range, Account dropdown, Type |
| Payment | Date range, Status, Amount range, Method |
| Content | Date range, Status, Site, Sector, Word count range |
| Tasks | Date range, Status, Cluster, Priority |
| AutomationRun | Date range, Status, Site, Success/Fail |
Phase 3: Monitoring & Dashboards (Week 5-6)
3.1 Celery Task Monitoring
Install django-celery-results:
pip install django-celery-results
Add to settings.py:
INSTALLED_APPS = [
# ...
'django_celery_results',
]
CELERY_RESULT_BACKEND = 'django-db'
CELERY_CACHE_BACKEND = 'django-cache'
Create Celery Task Admin:
File: backend/igny8_core/admin/celery_admin.py
from django.contrib import admin
from django_celery_results.models import TaskResult
@admin.register(TaskResult)
class CeleryTaskResultAdmin(admin.ModelAdmin):
list_display = [
'task_id',
'task_name',
'status',
'date_created',
'date_done',
'colored_status',
]
list_filter = [
'status',
'task_name',
('date_created', DateRangeFilter),
]
search_fields = ['task_id', 'task_name', 'task_args']
readonly_fields = ['task_id', 'task_name', 'task_args', 'task_kwargs', 'result', 'traceback']
actions = ['retry_failed_tasks', 'clear_old_tasks']
def colored_status(self, obj):
colors = {
'SUCCESS': 'green',
'FAILURE': 'red',
'PENDING': 'orange',
'STARTED': 'blue',
}
color = colors.get(obj.status, 'gray')
return format_html(
'<span style="color: {}; font-weight: bold;">{}</span>',
color,
obj.status
)
colored_status.short_description = 'Status'
def retry_failed_tasks(self, request, queryset):
"""Retry failed celery tasks"""
from celery import current_app
count = 0
for task in queryset.filter(status='FAILURE'):
try:
# Get task function and retry
task_func = current_app.tasks.get(task.task_name)
if task_func:
task_func.apply_async()
count += 1
except Exception as e:
self.message_user(request, f'Error retrying task {task.task_id}: {str(e)}', level='ERROR')
self.message_user(request, f'Retried {count} failed task(s)', level='SUCCESS')
retry_failed_tasks.short_description = 'Retry failed tasks'
Add to admin groups:
'🤖 AI & Automation': {
'models': [
# ...
('django_celery_results', 'TaskResult'),
],
},
3.2 Admin Dashboard with Metrics
Create custom admin index view:
File: backend/igny8_core/admin/dashboard.py
from django.contrib.admin.views.decorators import staff_member_required
from django.shortcuts import render
from django.db.models import Count, Sum, Q
from django.utils import timezone
from datetime import timedelta
@staff_member_required
def admin_dashboard(request):
"""Custom admin dashboard with key metrics"""
# Date ranges
today = timezone.now().date()
week_ago = today - timedelta(days=7)
month_ago = today - timedelta(days=30)
# Account metrics
from igny8_core.auth.models import Account
total_accounts = Account.objects.count()
active_accounts = Account.objects.filter(status='active').count()
low_credit_accounts = Account.objects.filter(credits__lt=100).count()
# Content metrics
from igny8_core.modules.writer.models import Content, Tasks
content_this_week = Content.objects.filter(created_at__gte=week_ago).count()
content_this_month = Content.objects.filter(created_at__gte=month_ago).count()
tasks_pending = Tasks.objects.filter(status='pending').count()
# Billing metrics
from igny8_core.modules.billing.models import Payment, CreditTransaction
pending_payments = Payment.objects.filter(status='pending_approval').count()
payments_this_month = Payment.objects.filter(
created_at__gte=month_ago,
status='succeeded'
).aggregate(total=Sum('amount'))['total'] or 0
credit_usage_this_month = CreditTransaction.objects.filter(
created_at__gte=month_ago,
transaction_type='deduction'
).aggregate(total=Sum('amount'))['total'] or 0
# Automation metrics
from igny8_core.business.automation.models import AutomationRun
automation_running = AutomationRun.objects.filter(status='running').count()
automation_failed = AutomationRun.objects.filter(
status='failed',
created_at__gte=week_ago
).count()
# WordPress sync metrics
from igny8_core.business.integration.models import SyncEvent
sync_failed = SyncEvent.objects.filter(
success=False,
created_at__gte=today
).count()
context = {
'title': 'Dashboard',
'accounts': {
'total': total_accounts,
'active': active_accounts,
'low_credit': low_credit_accounts,
},
'content': {
'this_week': content_this_week,
'this_month': content_this_month,
'tasks_pending': tasks_pending,
},
'billing': {
'pending_payments': pending_payments,
'payments_this_month': payments_this_month,
'credit_usage_this_month': abs(credit_usage_this_month),
},
'automation': {
'running': automation_running,
'failed_this_week': automation_failed,
},
'integration': {
'sync_failed_today': sync_failed,
},
}
return render(request, 'admin/dashboard.html', context)
Create dashboard template:
File: backend/igny8_core/templates/admin/dashboard.html
{% extends "admin/base_site.html" %}
{% load static %}
{% block content %}
<h1>IGNY8 Admin Dashboard</h1>
<!-- Unfold will automatically style these using its Tailwind-based theme -->
<div class="dashboard-grid">
<!-- Accounts Overview -->
<div class="dashboard-card">
<h2>Accounts</h2>
<div class="metric-row">
<div class="metric">
<span class="metric-value">{{ accounts.total }}</span>
<span class="metric-label">Total Accounts</span>
</div>
<div class="metric">
<span class="metric-value">{{ accounts.active }}</span>
<span class="metric-label">Active</span>
</div>
<div class="metric">
<span class="metric-value">{{ accounts.low_credit }}</span>
<span class="metric-label">Low Credits (< 100)</span>
</div>
</div>
</div>
<!-- Content Production -->
<div class="dashboard-card">
<h2>Content Production</h2>
<div class="metric-row">
<div class="metric">
<span class="metric-value">{{ content.this_week }}</span>
<span class="metric-label">This Week</span>
</div>
<div class="metric">
<span class="metric-value">{{ content.this_month }}</span>
<span class="metric-label">This Month</span>
</div>
<div class="metric">
<span class="metric-value">{{ content.tasks_pending }}</span>
<span class="metric-label">Tasks Pending</span>
</div>
</div>
</div>
<!-- Billing Overview -->
<div class="dashboard-card">
<h2>Billing</h2>
<div class="metric-row">
<div class="metric">
<span class="metric-value">{{ billing.pending_payments }}</span>
<span class="metric-label">Pending Approvals</span>
<a href="{% url 'admin:billing_payment_changelist' %}?status=pending_approval">
Review
</a>
</div>
<div class="metric">
<span class="metric-value">${{ billing.payments_this_month|floatformat:2 }}</span>
<span class="metric-label">Revenue This Month</span>
</div>
<div class="metric">
<span class="metric-value">{{ billing.credit_usage_this_month }}</span>
<span class="metric-label">Credits Used</span>
</div>
</div>
</div>
<!-- Automation Status -->
<div class="dashboard-card">
<h2>Automation</h2>
<div class="metric-row">
<div class="metric">
<span class="metric-value">{{ automation.running }}</span>
<span class="metric-label">Running Now</span>
</div>
<div class="metric">
<span class="metric-value">{{ automation.failed_this_week }}</span>
<span class="metric-label">Failed (7 days)</span>
{% if automation.failed_this_week > 0 %}
<a href="{% url 'admin:automation_automationrun_changelist' %}?status=failed">
Review
</a>
{% endif %}
</div>
</div>
</div>
<!-- Integration Health -->
<div class="dashboard-card">
<h2>Integration Health</h2>
<div class="metric-row">
<div class="metric">
<span class="metric-value">{{ integration.sync_failed_today }}</span>
<span class="metric-label">Failed Syncs Today</span>
{% if integration.sync_failed_today > 0 %}
<a href="{% url 'admin:integration_syncevent_changelist' %}?success=False">
Review
</a>
{% endif %}
</div>
</div>
</div>
</div>
{% endblock %}
Note: Unfold automatically provides styling via Tailwind CSS. No custom CSS needed - use Unfold's utility classes if customization is required.
Update admin site URLs:
# backend/igny8_core/admin/site.py
from .dashboard import admin_dashboard
class Igny8AdminSite(admin.AdminSite):
# ... existing code
def get_urls(self):
from django.urls import path
urls = super().get_urls()
custom_urls = [
path('dashboard/', self.admin_view(admin_dashboard), name='dashboard'),
]
return custom_urls + urls
def index(self, request, extra_context=None):
"""Redirect to custom dashboard"""
from django.shortcuts import redirect
return redirect('admin:dashboard')
3.3 Account Health Indicators
Add to Account Admin:
@admin.register(Account)
class AccountAdmin(AccountAdminMixin, admin.ModelAdmin):
# ... existing code
list_display = [
'name',
'slug',
'owner',
'plan',
'status',
'credits',
'health_indicator', # ADD
'created_at'
]
def health_indicator(self, obj):
"""Visual health score"""
score = 100
issues = []
# Check credit balance
if obj.credits < 50:
score -= 30
issues.append('Low credits')
elif obj.credits < 100:
score -= 15
# Check recent activity
from django.utils import timezone
from datetime import timedelta
week_ago = timezone.now() - timedelta(days=7)
from igny8_core.modules.writer.models import Content
recent_content = Content.objects.filter(
account=obj,
created_at__gte=week_ago
).count()
if recent_content == 0:
score -= 20
issues.append('No recent activity')
# Check failed automations
from igny8_core.business.automation.models import AutomationRun
failed_runs = AutomationRun.objects.filter(
site__account=obj,
status='failed',
created_at__gte=week_ago
).count()
if failed_runs > 0:
score -= 15
issues.append(f'{failed_runs} failed automation(s)')
# Determine color
if score >= 80:
color = 'green'
icon = '✓'
elif score >= 60:
color = 'orange'
icon = '⚠️'
else:
color = 'red'
icon = '✗'
issue_text = ', '.join(issues) if issues else 'Healthy'
# Use Unfold badge classes instead of inline styles
if score >= 80:
badge_class = 'success'
elif score >= 60:
badge_class = 'warning'
else:
badge_class = 'danger'
return format_html(
'<span class="badge badge-{}">{}</span>',
badge_class, issue_text
)
health_indicator.short_description = 'Health'
3.4 Alert System
Create alerts utility:
File: backend/igny8_core/admin/alerts.py
from django.core.cache import cache
from django.utils import timezone
from datetime import timedelta
class AdminAlerts:
"""System for admin alerts and notifications"""
@staticmethod
def get_alerts():
"""Get all active alerts"""
alerts = []
# Check for pending payments
from igny8_core.modules.billing.models import Payment
pending_payments = Payment.objects.filter(status='pending_approval').count()
if pending_payments > 0:
alerts.append({
'level': 'warning',
'message': f'{pending_payments} payment(s) awaiting approval',
'url': '/admin/billing/payment/?status=pending_approval',
'action': 'Review Payments'
})
# Check for low credit accounts
from igny8_core.auth.models import Account
low_credit_accounts = Account.objects.filter(
status='active',
credits__lt=100
).count()
if low_credit_accounts > 0:
alerts.append({
'level': 'info',
'message': f'{low_credit_accounts} account(s) with low credits',
'url': '/admin/igny8_core_auth/account/?credits__lt=100',
'action': 'View Accounts'
})
# Check for failed automations
from igny8_core.business.automation.models import AutomationRun
today = timezone.now().date()
failed_today = AutomationRun.objects.filter(
status='failed',
created_at__date=today
).count()
if failed_today > 0:
alerts.append({
'level': 'error',
'message': f'{failed_today} automation(s) failed today',
'url': '/admin/automation/automationrun/?status=failed',
'action': 'Review Failures'
})
# Check for failed syncs
from igny8_core.business.integration.models import SyncEvent
failed_syncs = SyncEvent.objects.filter(
success=False,
created_at__date=today
).count()
if failed_syncs > 5: # Only alert if more than 5
alerts.append({
'level': 'warning',
'message': f'{failed_syncs} WordPress sync failures today',
'url': '/admin/integration/syncevent/?success=False',
'action': 'Review Syncs'
})
return alerts
Add to dashboard template:
<!-- Add after <h1>IGNY8 Admin Dashboard</h1> -->
{% if alerts %}
<div class="alerts-section">
{% for alert in alerts %}
<div class="alert alert-{{ alert.level }}">
<span class="alert-message">{{ alert.message }}</span>
<a href="{{ alert.url }}" class="alert-action">{{ alert.action }}</a>
</div>
{% endfor %}
</div>
{% endif %}
Note: Use Unfold's alert styling classes. No custom CSS or emoji icons needed.
Phase 4: Analytics & Reporting (Week 7-8)
4.1 Business Intelligence Reports
Create reports module:
File: backend/igny8_core/admin/reports.py
from django.contrib.admin.views.decorators import staff_member_required
from django.shortcuts import render
from django.db.models import Count, Sum, Avg, Q
from django.utils import timezone
from datetime import timedelta
import json
@staff_member_required
def revenue_report(request):
"""Revenue and billing analytics"""
from igny8_core.modules.billing.models import Payment
from igny8_core.auth.models import Plan
# Date ranges
today = timezone.now()
months = []
monthly_revenue = []
for i in range(6):
month_start = today.replace(day=1) - timedelta(days=30*i)
month_end = month_start.replace(day=28) + timedelta(days=4)
revenue = Payment.objects.filter(
status='succeeded',
processed_at__gte=month_start,
processed_at__lt=month_end
).aggregate(total=Sum('amount'))['total'] or 0
months.insert(0, month_start.strftime('%b %Y'))
monthly_revenue.insert(0, float(revenue))
# Plan distribution
plan_distribution = Plan.objects.annotate(
account_count=Count('account')
).values('name', 'account_count')
# Payment method breakdown
payment_methods = Payment.objects.filter(
status='succeeded'
).values('payment_method').annotate(
count=Count('id'),
total=Sum('amount')
).order_by('-total')
context = {
'title': 'Revenue Report',
'months': json.dumps(months),
'monthly_revenue': json.dumps(monthly_revenue),
'plan_distribution': plan_distribution,
'payment_methods': payment_methods,
}
return render(request, 'admin/reports/revenue.html', context)
@staff_member_required
def usage_report(request):
"""Credit usage and AI operations analytics"""
from igny8_core.modules.billing.models import CreditUsageLog
# Usage by operation type
usage_by_operation = CreditUsageLog.objects.values(
'operation_type'
).annotate(
total_credits=Sum('credits_used'),
total_cost=Sum('cost_usd'),
operation_count=Count('id')
).order_by('-total_credits')
# Top credit consumers
from django.db.models import Count
top_consumers = CreditUsageLog.objects.values(
'account__name'
).annotate(
total_credits=Sum('credits_used'),
operation_count=Count('id')
).order_by('-total_credits')[:10]
# Model usage distribution
model_usage = CreditUsageLog.objects.values(
'model_used'
).annotate(
usage_count=Count('id')
).order_by('-usage_count')
context = {
'title': 'Usage Report',
'usage_by_operation': usage_by_operation,
'top_consumers': top_consumers,
'model_usage': model_usage,
}
return render(request, 'admin/reports/usage.html', context)
@staff_member_required
def content_report(request):
"""Content production analytics"""
from igny8_core.modules.writer.models import Content, Tasks
# Content by type
content_by_type = Content.objects.values(
'content_type'
).annotate(count=Count('id')).order_by('-count')
# Production timeline (last 30 days)
days = []
daily_counts = []
for i in range(30):
day = timezone.now().date() - timedelta(days=i)
count = Content.objects.filter(created_at__date=day).count()
days.insert(0, day.strftime('%m/%d'))
daily_counts.insert(0, count)
# Average word count by content type
avg_words = Content.objects.values('content_type').annotate(
avg_words=Avg('word_count')
).order_by('-avg_words')
# Task completion rate
total_tasks = Tasks.objects.count()
completed_tasks = Tasks.objects.filter(status='completed').count()
completion_rate = (completed_tasks / total_tasks * 100) if total_tasks > 0 else 0
context = {
'title': 'Content Production Report',
'content_by_type': content_by_type,
'days': json.dumps(days),
'daily_counts': json.dumps(daily_counts),
'avg_words': avg_words,
'completion_rate': completion_rate,
}
return render(request, 'admin/reports/content.html', context)
Add report links to admin site:
# In Igny8AdminSite.get_urls()
custom_urls = [
path('dashboard/', self.admin_view(admin_dashboard), name='dashboard'),
path('reports/revenue/', self.admin_view(revenue_report), name='report_revenue'),
path('reports/usage/', self.admin_view(usage_report), name='report_usage'),
path('reports/content/', self.admin_view(content_report), name='report_content'),
]
4.2 Data Quality Dashboard
Create data quality checker:
@staff_member_required
def data_quality_report(request):
"""Check data quality and integrity"""
issues = []
# Orphaned content (no site)
from igny8_core.modules.writer.models import Content
orphaned_content = Content.objects.filter(site__isnull=True).count()
if orphaned_content > 0:
issues.append({
'severity': 'warning',
'type': 'Orphaned Records',
'count': orphaned_content,
'description': 'Content items without assigned site',
'action_url': '/admin/writer/content/?site__isnull=True'
})
# Tasks without clusters
from igny8_core.modules.writer.models import Tasks
tasks_no_cluster = Tasks.objects.filter(cluster__isnull=True).count()
if tasks_no_cluster > 0:
issues.append({
'severity': 'info',
'type': 'Missing Relationships',
'count': tasks_no_cluster,
'description': 'Tasks without assigned cluster',
'action_url': '/admin/writer/tasks/?cluster__isnull=True'
})
# Accounts with negative credits
from igny8_core.auth.models import Account
negative_credits = Account.objects.filter(credits__lt=0).count()
if negative_credits > 0:
issues.append({
'severity': 'error',
'type': 'Data Integrity',
'count': negative_credits,
'description': 'Accounts with negative credit balance',
'action_url': '/admin/igny8_core_auth/account/?credits__lt=0'
})
# Duplicate keywords
from igny8_core.modules.planner.models import Keywords
from django.db.models import Count
duplicates = Keywords.objects.values('keyword', 'site', 'sector').annotate(
count=Count('id')
).filter(count__gt=1).count()
if duplicates > 0:
issues.append({
'severity': 'warning',
'type': 'Duplicates',
'count': duplicates,
'description': 'Duplicate keywords for same site/sector',
'action_url': '/admin/planner/keywords/'
})
# Content without SEO data
no_seo = Content.objects.filter(
Q(meta_title__isnull=True) | Q(meta_title='') |
Q(meta_description__isnull=True) | Q(meta_description='')
).count()
if no_seo > 0:
issues.append({
'severity': 'info',
'type': 'Incomplete Data',
'count': no_seo,
'description': 'Content missing SEO metadata',
'action_url': '/admin/writer/content/'
})
context = {
'title': 'Data Quality Report',
'issues': issues,
'total_issues': len(issues),
}
return render(request, 'admin/reports/data_quality.html', context)
Phase 5: Advanced Features (Week 9-10)
5.1 Inline Editing
Enable list_editable for common fields:
# Tasks Admin
class TasksAdmin(SiteSectorAdminMixin, admin.ModelAdmin):
list_editable = ['status', 'cluster'] # Quick edit in list view
# Keywords Admin
class KeywordsAdmin(SiteSectorAdminMixin, admin.ModelAdmin):
list_editable = ['cluster', 'status']
# Payment Admin - already has workflow, keep as-is
5.2 Enhanced Audit Trail
Install django-simple-history:
pip install django-simple-history
Add to critical models:
from simple_history.models import HistoricalRecords
class Payment(models.Model):
# ... existing fields
history = HistoricalRecords()
class Account(AccountBaseModel):
# ... existing fields
history = HistoricalRecords()
class CreditCostConfig(models.Model):
# ... existing fields
history = HistoricalRecords()
Register history admin:
from simple_history.admin import SimpleHistoryAdmin
@admin.register(Payment)
class PaymentAdmin(SimpleHistoryAdmin, AccountAdminMixin, admin.ModelAdmin):
# ... existing code
# This adds a "History" button to view all changes
5.3 Permission Groups
Create permission groups:
# backend/igny8_core/management/commands/create_admin_groups.py
from django.core.management.base import BaseCommand
from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
class Command(BaseCommand):
help = 'Create admin permission groups'
def handle(self, *args, **kwargs):
# Content Manager Group
content_manager, _ = Group.objects.get_or_create(name='Content Manager')
content_perms = Permission.objects.filter(
content_type__app_label__in=['writer', 'planner'],
codename__in=['view_content', 'change_content', 'view_tasks', 'change_tasks']
)
content_manager.permissions.set(content_perms)
# Billing Administrator Group
billing_admin, _ = Group.objects.get_or_create(name='Billing Administrator')
billing_perms = Permission.objects.filter(
content_type__app_label='billing'
)
billing_admin.permissions.set(billing_perms)
# Support Agent Group (Read-Only)
support_agent, _ = Group.objects.get_or_create(name='Support Agent')
support_perms = Permission.objects.filter(
codename__startswith='view_'
)
support_agent.permissions.set(support_perms)
self.stdout.write(self.style.SUCCESS('Successfully created admin groups'))
Implementation Checklist
✅ Phase 0: Foundation (COMPLETED - Dec 14, 2025)
- Install and configure Unfold theme
- Fix all admin classes to inherit from Unfold ModelAdmin
- Fix UserAdmin popup styling (multiple inheritance)
- Configure UNFOLD settings in settings.py
- Update middleware and INSTALLED_APPS
- Test all containers running
Phase 1: Configuration Cleanup (Week 1) - IN PROGRESS
- Fix UserAdmin popup styling
- Remove unused site_building models from admin site config
- Remove duplicate model registrations (keep modules/ versions)
- Add PlanLimitUsage to Billing & Accounts group
- Verify all model links work
- Test admin configuration
Phase 2: Bulk Operations & Export (Week 2-3) - NOT STARTED
- Add bulk status update actions to Tasks admin
- Add bulk operations to Content admin
- Add bulk operations to Keywords admin
- Add export action to Payments admin
- Install django-import-export
- Create Resource classes for Tasks, Content, Keywords, Payments
- Add export buttons to admin list views
- Install django-admin-rangefilter
- Add date range filters to key models
- Configure autocomplete_fields for large foreign keys
- Test all bulk operations
- Test export functionality
✅ Phase 3: Monitoring & Dashboards (COMPLETED - Dec 14, 2025)
- Install django-celery-results
- Configure Celery to use django-db backend
- Create CeleryTaskResultAdmin with colored status
- Create CeleryGroupResultAdmin with colored status
- Add retry_failed_tasks action
- Add clear_old_tasks action
- Create admin_dashboard view function
- Create dashboard.html template with metrics
- Add dashboard route to admin site URLs
- Redirect admin index to dashboard
- Add health_indicator to Account admin
- Add health_details to Account admin
- Create AdminAlerts utility class
- Add alerts section to dashboard template
- Fix execution_time format_html issue
- Test dashboard metrics accuracy
- Test alert system functionality
- Verify all Celery admin pages work (200 status)
✅ Phase 4: Analytics & Reporting (COMPLETED - Dec 15, 2025)
- Create reports.py module
- Implement revenue_report view
- Implement usage_report view
- Implement content_report view
- Implement data_quality_report view
- Create report templates (revenue, usage, content, data_quality)
- Add chart.js for visualizations
- Add report routes to admin site URLs
- Add report links to admin sidebar navigation
- Create report permission checks
- Test all reports with real data (operational task)
- Optimize report queries for performance (operational task)
✅ Phase 5: Advanced Features (COMPLETED - Dec 15, 2025)
- Enable list_editable for Tasks and Keywords
- django-simple-history already installed
- Add HistoricalRecords to Payment model
- Add HistoricalRecords to Account model
- Add HistoricalRecords to CreditCostConfig model
- Run migrations for history tables
- Update admins to use SimpleHistoryAdmin
- Create create_admin_groups management command
- Define permission groups (Content Manager, Billing Admin, Support Agent)
- Assign permissions to groups
- Test permission restrictions (operational task)
- Document permission group usage (operational task)
Technical Requirements
Python Packages
# Already installed:
django-unfold==0.73.1 # Modern admin theme (✅ INSTALLED)
django-import-export==3.3.1 # CSV/Excel import/export (✅ INSTALLED)
django-celery-results==2.5.1 # Celery monitoring (✅ INSTALLED)
django-simple-history==3.4.0 # Audit trail (✅ INSTALLED)
# No additional packages needed for styling - Unfold handles everything
Settings Configuration
# backend/igny8_core/settings.py
# ✅ Already configured properly
INSTALLED_APPS = [
'unfold', # ✅ Already installed - Must be before django.contrib.admin
'unfold.contrib.filters', # ✅ Already installed
'unfold.contrib.import_export', # ✅ Already installed
'unfold.contrib.simple_history', # ✅ Already installed
'igny8_core.admin.apps.Igny8AdminConfig',
# ... rest of apps
'import_export', # ✅ Already installed
'django_celery_results', # ✅ Already installed
'simple_history', # ✅ Already installed
]
# Celery Results - ✅ Already configured
CELERY_RESULT_BACKEND = 'django-db'
CELERY_CACHE_BACKEND = 'django-cache'
# History Middleware - ✅ Already configured
MIDDLEWARE = [
# ... existing middleware
'simple_history.middleware.HistoryRequestMiddleware',
]
# Import/Export Settings
IMPORT_EXPORT_USE_TRANSACTIONS = True
# UNFOLD configuration - ✅ Already configured in settings.py
Database Migrations
# Already completed - no additional migrations needed for Unfold
# Unfold uses Django's existing admin tables
# For future features:
# python manage.py migrate django_celery_results # When adding Celery monitoring
# python manage.py migrate simple_history # When adding history to models
Template Structure
backend/igny8_core/templates/
├── admin/
│ ├── base_site.html
│ ├── dashboard.html
│ └── reports/
│ ├── revenue.html
│ ├── usage.html
│ ├── content.html
│ └── data_quality.html
Testing Plan
Manual Testing Checklist
- Admin login works
- Dashboard displays correctly
- All sidebar groups show correct models
- No 404 errors for any admin links
- Bulk actions work on all enabled models
- CSV export generates valid files
- Date range filters work correctly
- Autocomplete fields load data
- Celery task monitoring shows tasks
- Dashboard metrics are accurate
- Alerts show for real issues
- Reports generate without errors
- Charts render correctly
- History tracking works for enabled models
- Permission groups restrict access correctly
Performance Testing
- Dashboard loads in < 2 seconds
- List views with 1000+ records paginate correctly
- Bulk operations on 100+ records complete
- Export of 1000+ records completes
- Reports with large datasets load within 5 seconds
- No N+1 query issues in list views
Rollback Plan
If issues arise during implementation:
-
Unfold Issues (unlikely): Already stable and working
- Unfold is well-tested and production-ready
- No custom styling to break
- All standard Django admin features work
-
Duplicate Registration Issues:
- Keep
modules/admin files active - Comment out
business/admin registrations
- Keep
-
Performance Issues:
- Disable expensive dashboard queries
- Use caching for metrics
- Reduce pagination size
-
Database Issues:
- All migrations are reversible
- Backup before major changes
- Test on staging first
Success Metrics
Track these metrics to measure improvement success:
-
Time Savings
- Payment approval time: Target < 2 minutes
- Content review time: Target < 5 minutes per item
- Report generation time: Target < 30 seconds
-
User Satisfaction
- Admin user feedback: Target 8+/10
- Feature usage: 80%+ of new features used weekly
-
Data Quality
- Orphaned records: Reduce by 90%
- Duplicate data: Reduce by 80%
- Data completeness: Increase to 95%+
-
Operational Efficiency
- Failed automation detection: < 1 hour
- Payment processing: Same-day completion
- Account health monitoring: 100% visibility
Maintenance & Support
Regular Maintenance Tasks
-
Weekly:
- Review dashboard alerts
- Check failed celery tasks
- Monitor data quality issues
-
Monthly:
- Review permission group assignments
- Update credit cost configs if needed
- Clean up old celery task results
- Archive old audit logs
-
Quarterly:
- Review and optimize slow admin queries
- Update admin theme if needed
- Review and update permission groups
- Analyze usage patterns for improvements
Documentation Updates
Keep these docs updated:
- Admin user guide with new features
- Permission group definitions
- Report interpretation guides
- Troubleshooting common issues
Future Enhancements (Post-Launch)
-
AI-Powered Insights
- Anomaly detection in usage patterns
- Predictive account churn analysis
- Automated optimization recommendations
-
Advanced Automation
- Workflow builder for common tasks
- Scheduled report delivery
- Auto-responses to common issues
-
Mobile Admin
- Responsive mobile design
- Mobile-optimized dashboards
- Push notifications for critical alerts
-
Integration Enhancements
- Slack/Discord alert integration
- Zapier/Make.com workflow triggers
- API for external admin tools
Conclusion
This comprehensive plan transforms the Django admin from a basic management tool into a powerful operational hub. The phased approach ensures steady progress while minimizing disruption. Focus on Phases 1-3 for immediate operational impact, then expand to analytics and advanced features in Phases 4-5.
Estimated Total Effort: 2-4 weeks with 1-2 developers (reduced since styling is already complete via Unfold)
Priority: 🔴 High - Critical for efficient operations at scale
Dependencies: Should be implemented after Plan Management (#2) is complete