django phase2.3.4.
This commit is contained in:
122
backend/igny8_core/admin/alerts.py
Normal file
122
backend/igny8_core/admin/alerts.py
Normal file
@@ -0,0 +1,122 @@
|
||||
"""
|
||||
Admin Alert System
|
||||
"""
|
||||
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 = []
|
||||
today = timezone.now().date()
|
||||
|
||||
# Check for pending payments
|
||||
from igny8_core.business.billing.models import Payment
|
||||
pending_payments = Payment.objects.filter(status='pending_approval').count()
|
||||
if pending_payments > 0:
|
||||
alerts.append({
|
||||
'level': 'warning',
|
||||
'icon': '⚠️',
|
||||
'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',
|
||||
'icon': 'ℹ️',
|
||||
'message': f'{low_credit_accounts} account(s) with low credits',
|
||||
'url': '/admin/igny8_core_auth/account/?credits__lt=100',
|
||||
'action': 'View Accounts'
|
||||
})
|
||||
|
||||
# Check for very low credits (critical)
|
||||
critical_credit_accounts = Account.objects.filter(
|
||||
status='active',
|
||||
credits__lt=10
|
||||
).count()
|
||||
if critical_credit_accounts > 0:
|
||||
alerts.append({
|
||||
'level': 'error',
|
||||
'icon': '🔴',
|
||||
'message': f'{critical_credit_accounts} account(s) with critical low credits (< 10)',
|
||||
'url': '/admin/igny8_core_auth/account/?credits__lt=10',
|
||||
'action': 'Urgent Review'
|
||||
})
|
||||
|
||||
# Check for failed automations
|
||||
from igny8_core.business.automation.models import AutomationRun
|
||||
failed_today = AutomationRun.objects.filter(
|
||||
status='failed',
|
||||
started_at__date=today
|
||||
).count()
|
||||
if failed_today > 0:
|
||||
alerts.append({
|
||||
'level': 'error',
|
||||
'icon': '🔴',
|
||||
'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',
|
||||
'icon': '⚠️',
|
||||
'message': f'{failed_syncs} WordPress sync failures today',
|
||||
'url': '/admin/integration/syncevent/?success=False',
|
||||
'action': 'Review Syncs'
|
||||
})
|
||||
|
||||
# Check for failed Celery tasks
|
||||
try:
|
||||
from django_celery_results.models import TaskResult
|
||||
celery_failed = TaskResult.objects.filter(
|
||||
status='FAILURE',
|
||||
date_created__date=today
|
||||
).count()
|
||||
if celery_failed > 0:
|
||||
alerts.append({
|
||||
'level': 'error',
|
||||
'icon': '🔴',
|
||||
'message': f'{celery_failed} Celery task(s) failed today',
|
||||
'url': '/admin/django_celery_results/taskresult/?status=FAILURE',
|
||||
'action': 'Review Tasks'
|
||||
})
|
||||
except:
|
||||
pass
|
||||
|
||||
# Check for stale pending tasks (older than 24 hours)
|
||||
from igny8_core.modules.writer.models import Tasks
|
||||
yesterday = today - timedelta(days=1)
|
||||
stale_tasks = Tasks.objects.filter(
|
||||
status='pending',
|
||||
created_at__date__lte=yesterday
|
||||
).count()
|
||||
if stale_tasks > 10:
|
||||
alerts.append({
|
||||
'level': 'info',
|
||||
'icon': 'ℹ️',
|
||||
'message': f'{stale_tasks} tasks pending for more than 24 hours',
|
||||
'url': '/admin/writer/tasks/?status=pending',
|
||||
'action': 'Review Tasks'
|
||||
})
|
||||
|
||||
return alerts
|
||||
@@ -39,5 +39,28 @@ class Igny8AdminConfig(AdminConfig):
|
||||
_safe_register(Group, admin.ModelAdmin)
|
||||
_safe_register(ContentType, ReadOnlyAdmin)
|
||||
_safe_register(Session, ReadOnlyAdmin)
|
||||
|
||||
# Import and setup enhanced Celery task monitoring
|
||||
self._setup_celery_admin()
|
||||
|
||||
def _setup_celery_admin(self):
|
||||
"""Setup enhanced Celery admin with proper unregister/register"""
|
||||
try:
|
||||
from django_celery_results.models import TaskResult
|
||||
from igny8_core.admin.celery_admin import CeleryTaskResultAdmin
|
||||
|
||||
# Unregister the default TaskResult admin
|
||||
try:
|
||||
admin.site.unregister(TaskResult)
|
||||
except admin.sites.NotRegistered:
|
||||
pass
|
||||
|
||||
# Register our enhanced version
|
||||
admin.site.register(TaskResult, CeleryTaskResultAdmin)
|
||||
except Exception as e:
|
||||
# Log the error but don't crash the app
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.warning(f"Could not setup enhanced Celery admin: {e}")
|
||||
|
||||
|
||||
|
||||
154
backend/igny8_core/admin/celery_admin.py
Normal file
154
backend/igny8_core/admin/celery_admin.py
Normal file
@@ -0,0 +1,154 @@
|
||||
"""
|
||||
Celery Task Monitoring Admin
|
||||
"""
|
||||
from django.contrib import admin
|
||||
from django.utils.html import format_html
|
||||
from django.contrib import messages
|
||||
from django_celery_results.models import TaskResult
|
||||
from rangefilter.filters import DateRangeFilter
|
||||
|
||||
|
||||
class CeleryTaskResultAdmin(admin.ModelAdmin):
|
||||
"""Admin interface for monitoring Celery tasks"""
|
||||
|
||||
list_display = [
|
||||
'task_id',
|
||||
'task_name',
|
||||
'colored_status',
|
||||
'date_created',
|
||||
'date_done',
|
||||
'execution_time',
|
||||
]
|
||||
list_filter = [
|
||||
'status',
|
||||
'task_name',
|
||||
('date_created', DateRangeFilter),
|
||||
('date_done', DateRangeFilter),
|
||||
]
|
||||
search_fields = ['task_id', 'task_name', 'task_args']
|
||||
readonly_fields = [
|
||||
'task_id', 'task_name', 'task_args', 'task_kwargs',
|
||||
'result', 'traceback', 'date_created', 'date_done',
|
||||
'colored_status', 'execution_time'
|
||||
]
|
||||
date_hierarchy = 'date_created'
|
||||
ordering = ['-date_created']
|
||||
|
||||
actions = ['retry_failed_tasks', 'clear_old_tasks']
|
||||
|
||||
fieldsets = (
|
||||
('Task Information', {
|
||||
'fields': ('task_id', 'task_name', 'colored_status')
|
||||
}),
|
||||
('Execution Details', {
|
||||
'fields': ('date_created', 'date_done', 'execution_time')
|
||||
}),
|
||||
('Task Arguments', {
|
||||
'fields': ('task_args', 'task_kwargs'),
|
||||
'classes': ('collapse',)
|
||||
}),
|
||||
('Result & Errors', {
|
||||
'fields': ('result', 'traceback'),
|
||||
'classes': ('collapse',)
|
||||
}),
|
||||
)
|
||||
|
||||
def colored_status(self, obj):
|
||||
"""Display status with color coding"""
|
||||
colors = {
|
||||
'SUCCESS': '#0bbf87', # IGNY8 success green
|
||||
'FAILURE': '#ef4444', # IGNY8 danger red
|
||||
'PENDING': '#ff7a00', # IGNY8 warning orange
|
||||
'STARTED': '#0693e3', # IGNY8 primary blue
|
||||
'RETRY': '#5d4ae3', # IGNY8 purple
|
||||
}
|
||||
color = colors.get(obj.status, '#64748b') # Default gray
|
||||
|
||||
return format_html(
|
||||
'<span style="color: {}; font-weight: bold; font-size: 14px;">{}</span>',
|
||||
color,
|
||||
obj.status
|
||||
)
|
||||
colored_status.short_description = 'Status'
|
||||
|
||||
def execution_time(self, obj):
|
||||
"""Calculate and display execution time"""
|
||||
if obj.date_done and obj.date_created:
|
||||
duration = obj.date_done - obj.date_created
|
||||
seconds = duration.total_seconds()
|
||||
|
||||
if seconds < 1:
|
||||
return format_html('<span style="color: #0bbf87;">{:.2f}ms</span>', seconds * 1000)
|
||||
elif seconds < 60:
|
||||
return format_html('<span style="color: #0693e3;">{:.2f}s</span>', seconds)
|
||||
else:
|
||||
minutes = seconds / 60
|
||||
return format_html('<span style="color: #ff7a00;">{:.1f}m</span>', minutes)
|
||||
return '-'
|
||||
execution_time.short_description = 'Duration'
|
||||
|
||||
def retry_failed_tasks(self, request, queryset):
|
||||
"""Retry failed celery tasks"""
|
||||
from celery import current_app
|
||||
|
||||
failed_tasks = queryset.filter(status='FAILURE')
|
||||
count = 0
|
||||
errors = []
|
||||
|
||||
for task in failed_tasks:
|
||||
try:
|
||||
# Get task function
|
||||
task_func = current_app.tasks.get(task.task_name)
|
||||
if task_func:
|
||||
# Parse task args and kwargs
|
||||
import ast
|
||||
try:
|
||||
args = ast.literal_eval(task.task_args) if task.task_args else []
|
||||
kwargs = ast.literal_eval(task.task_kwargs) if task.task_kwargs else {}
|
||||
except:
|
||||
args = []
|
||||
kwargs = {}
|
||||
|
||||
# Retry the task
|
||||
task_func.apply_async(args=args, kwargs=kwargs)
|
||||
count += 1
|
||||
else:
|
||||
errors.append(f'Task function not found: {task.task_name}')
|
||||
except Exception as e:
|
||||
errors.append(f'Error retrying {task.task_id}: {str(e)}')
|
||||
|
||||
if count > 0:
|
||||
self.message_user(request, f'✅ Retried {count} failed task(s)', messages.SUCCESS)
|
||||
|
||||
if errors:
|
||||
for error in errors[:5]: # Show max 5 errors
|
||||
self.message_user(request, f'⚠️ {error}', messages.WARNING)
|
||||
|
||||
retry_failed_tasks.short_description = '🔄 Retry Failed Tasks'
|
||||
|
||||
def clear_old_tasks(self, request, queryset):
|
||||
"""Clear old completed tasks"""
|
||||
from datetime import timedelta
|
||||
from django.utils import timezone
|
||||
|
||||
# Delete tasks older than 30 days
|
||||
cutoff_date = timezone.now() - timedelta(days=30)
|
||||
old_tasks = queryset.filter(
|
||||
date_created__lt=cutoff_date,
|
||||
status__in=['SUCCESS', 'FAILURE']
|
||||
)
|
||||
|
||||
count = old_tasks.count()
|
||||
old_tasks.delete()
|
||||
|
||||
self.message_user(request, f'🗑️ Cleared {count} old task(s)', messages.SUCCESS)
|
||||
|
||||
clear_old_tasks.short_description = '🗑️ Clear Old Tasks (30+ days)'
|
||||
|
||||
def has_add_permission(self, request):
|
||||
"""Disable manual task creation"""
|
||||
return False
|
||||
|
||||
def has_change_permission(self, request, obj=None):
|
||||
"""Make read-only"""
|
||||
return False
|
||||
112
backend/igny8_core/admin/dashboard.py
Normal file
112
backend/igny8_core/admin/dashboard.py
Normal file
@@ -0,0 +1,112 @@
|
||||
"""
|
||||
Custom Admin Dashboard with Key Metrics
|
||||
"""
|
||||
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 operational 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(
|
||||
status='active',
|
||||
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()
|
||||
tasks_in_progress = Tasks.objects.filter(status='in_progress').count()
|
||||
|
||||
# Billing metrics
|
||||
from igny8_core.business.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',
|
||||
started_at__gte=week_ago
|
||||
).count()
|
||||
|
||||
# WordPress sync metrics
|
||||
from igny8_core.business.integration.models import SyncEvent
|
||||
sync_failed_today = SyncEvent.objects.filter(
|
||||
success=False,
|
||||
created_at__date=today
|
||||
).count()
|
||||
|
||||
# Celery task metrics
|
||||
try:
|
||||
from django_celery_results.models import TaskResult
|
||||
celery_failed = TaskResult.objects.filter(
|
||||
status='FAILURE',
|
||||
date_created__date=today
|
||||
).count()
|
||||
celery_pending = TaskResult.objects.filter(status='PENDING').count()
|
||||
except:
|
||||
celery_failed = 0
|
||||
celery_pending = 0
|
||||
|
||||
# Get alerts
|
||||
from .alerts import AdminAlerts
|
||||
alerts = AdminAlerts.get_alerts()
|
||||
|
||||
context = {
|
||||
'title': 'IGNY8 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,
|
||||
'tasks_in_progress': tasks_in_progress,
|
||||
},
|
||||
'billing': {
|
||||
'pending_payments': pending_payments,
|
||||
'payments_this_month': float(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_today,
|
||||
},
|
||||
'celery': {
|
||||
'failed_today': celery_failed,
|
||||
'pending': celery_pending,
|
||||
},
|
||||
'alerts': alerts,
|
||||
}
|
||||
|
||||
return render(request, 'admin/dashboard.html', context)
|
||||
@@ -4,6 +4,8 @@ Custom AdminSite for IGNY8 to organize models into proper groups
|
||||
from django.contrib import admin
|
||||
from django.contrib.admin.apps import AdminConfig
|
||||
from django.apps import apps
|
||||
from django.urls import path
|
||||
from django.shortcuts import redirect
|
||||
|
||||
|
||||
class Igny8AdminSite(admin.AdminSite):
|
||||
@@ -20,6 +22,19 @@ class Igny8AdminSite(admin.AdminSite):
|
||||
site_header = 'IGNY8 Administration'
|
||||
site_title = 'IGNY8 Admin'
|
||||
index_title = 'IGNY8 Administration'
|
||||
|
||||
def get_urls(self):
|
||||
"""Add dashboard URL"""
|
||||
urls = super().get_urls()
|
||||
custom_urls = [
|
||||
path('dashboard/', self.dashboard_view, name='dashboard'),
|
||||
]
|
||||
return custom_urls + urls
|
||||
|
||||
def dashboard_view(self, request):
|
||||
"""Dashboard view wrapper"""
|
||||
from igny8_core.admin.dashboard import admin_dashboard
|
||||
return admin_dashboard(request)
|
||||
|
||||
def get_app_list(self, request):
|
||||
"""
|
||||
@@ -113,7 +128,13 @@ class Igny8AdminSite(admin.AdminSite):
|
||||
('system', 'SystemStatus'),
|
||||
],
|
||||
},
|
||||
'🔧 Django System': {
|
||||
'<EFBFBD> Monitoring & Tasks': {
|
||||
'models': [
|
||||
('django_celery_results', 'TaskResult'),
|
||||
('django_celery_results', 'GroupResult'),
|
||||
],
|
||||
},
|
||||
'<EFBFBD>🔧 Django System': {
|
||||
'models': [
|
||||
('admin', 'LogEntry'),
|
||||
('auth', 'Group'),
|
||||
|
||||
@@ -145,10 +145,10 @@ class PlanAdmin(admin.ModelAdmin):
|
||||
@admin.register(Account)
|
||||
class AccountAdmin(AccountAdminMixin, admin.ModelAdmin):
|
||||
form = AccountAdminForm
|
||||
list_display = ['name', 'slug', 'owner', 'plan', 'status', 'credits', 'created_at']
|
||||
list_display = ['name', 'slug', 'owner', 'plan', 'status', 'health_indicator', 'credits', 'created_at']
|
||||
list_filter = ['status', 'plan']
|
||||
search_fields = ['name', 'slug']
|
||||
readonly_fields = ['created_at', 'updated_at']
|
||||
readonly_fields = ['created_at', 'updated_at', 'health_indicator', 'health_details']
|
||||
|
||||
def get_queryset(self, request):
|
||||
"""Override to filter by account for non-superusers"""
|
||||
@@ -167,6 +167,137 @@ class AccountAdmin(AccountAdminMixin, admin.ModelAdmin):
|
||||
# If account access fails (e.g., column mismatch), return empty
|
||||
pass
|
||||
return qs.none()
|
||||
|
||||
def health_indicator(self, obj):
|
||||
"""Display health status with visual indicator"""
|
||||
from django.utils.html import format_html
|
||||
from django.utils import timezone
|
||||
from datetime import timedelta
|
||||
|
||||
# Check credits
|
||||
if obj.credits < 10:
|
||||
status = 'critical'
|
||||
icon = '🔴'
|
||||
message = 'Critical: Very low credits'
|
||||
elif obj.credits < 100:
|
||||
status = 'warning'
|
||||
icon = '⚠️'
|
||||
message = 'Warning: Low credits'
|
||||
else:
|
||||
status = 'good'
|
||||
icon = '✅'
|
||||
message = 'Good'
|
||||
|
||||
# Check for recent failed automations
|
||||
try:
|
||||
from igny8_core.business.automation.models import AutomationRun
|
||||
week_ago = timezone.now() - timedelta(days=7)
|
||||
failed_runs = AutomationRun.objects.filter(
|
||||
account=obj,
|
||||
status='failed',
|
||||
created_at__gte=week_ago
|
||||
).count()
|
||||
|
||||
if failed_runs > 5:
|
||||
status = 'critical'
|
||||
icon = '🔴'
|
||||
message = f'Critical: {failed_runs} automation failures'
|
||||
elif failed_runs > 0:
|
||||
if status == 'good':
|
||||
status = 'warning'
|
||||
icon = '⚠️'
|
||||
message = f'Warning: {failed_runs} automation failures'
|
||||
except:
|
||||
pass
|
||||
|
||||
# Check account status
|
||||
if obj.status != 'active':
|
||||
status = 'critical'
|
||||
icon = '🔴'
|
||||
message = f'Critical: Account {obj.status}'
|
||||
|
||||
colors = {
|
||||
'good': '#0bbf87',
|
||||
'warning': '#ff7a00',
|
||||
'critical': '#ef4444'
|
||||
}
|
||||
|
||||
return format_html(
|
||||
'<span style="font-size: 16px;">{}</span> <span style="color: {}; font-weight: 600;">{}</span>',
|
||||
icon, colors[status], message
|
||||
)
|
||||
health_indicator.short_description = 'Health'
|
||||
|
||||
def health_details(self, obj):
|
||||
"""Detailed health information"""
|
||||
from django.utils.html import format_html
|
||||
from django.utils import timezone
|
||||
from datetime import timedelta
|
||||
|
||||
details = []
|
||||
|
||||
# Credits status
|
||||
if obj.credits < 10:
|
||||
details.append(f'🔴 <b>Critical:</b> Only {obj.credits} credits remaining')
|
||||
elif obj.credits < 100:
|
||||
details.append(f'⚠️ <b>Warning:</b> Only {obj.credits} credits remaining')
|
||||
else:
|
||||
details.append(f'✅ <b>Credits:</b> {obj.credits} available')
|
||||
|
||||
# Recent activity
|
||||
try:
|
||||
from igny8_core.modules.writer.models import Content
|
||||
week_ago = timezone.now() - timedelta(days=7)
|
||||
recent_content = Content.objects.filter(
|
||||
site__account=obj,
|
||||
created_at__gte=week_ago
|
||||
).count()
|
||||
details.append(f'📚 <b>Activity:</b> {recent_content} content pieces created this week')
|
||||
except:
|
||||
pass
|
||||
|
||||
# Failed automations
|
||||
try:
|
||||
from igny8_core.business.automation.models import AutomationRun
|
||||
week_ago = timezone.now() - timedelta(days=7)
|
||||
failed_runs = AutomationRun.objects.filter(
|
||||
account=obj,
|
||||
status='failed',
|
||||
created_at__gte=week_ago
|
||||
).count()
|
||||
|
||||
if failed_runs > 0:
|
||||
details.append(f'🔴 <b>Automations:</b> {failed_runs} failures this week')
|
||||
else:
|
||||
details.append(f'✅ <b>Automations:</b> No failures this week')
|
||||
except:
|
||||
pass
|
||||
|
||||
# Failed syncs
|
||||
try:
|
||||
from igny8_core.business.integration.models import SyncEvent
|
||||
today = timezone.now().date()
|
||||
failed_syncs = SyncEvent.objects.filter(
|
||||
site__account=obj,
|
||||
success=False,
|
||||
created_at__date=today
|
||||
).count()
|
||||
|
||||
if failed_syncs > 0:
|
||||
details.append(f'⚠️ <b>Syncs:</b> {failed_syncs} failures today')
|
||||
else:
|
||||
details.append(f'✅ <b>Syncs:</b> No failures today')
|
||||
except:
|
||||
pass
|
||||
|
||||
# Account status
|
||||
if obj.status == 'active':
|
||||
details.append(f'✅ <b>Status:</b> Active')
|
||||
else:
|
||||
details.append(f'🔴 <b>Status:</b> {obj.status.title()}')
|
||||
|
||||
return format_html('<br>'.join(details))
|
||||
health_details.short_description = 'Health Details'
|
||||
|
||||
def has_delete_permission(self, request, obj=None):
|
||||
if obj and getattr(obj, 'slug', '') == 'aws-admin':
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
from django.contrib import admin
|
||||
from django.contrib import messages
|
||||
from igny8_core.admin.base import SiteSectorAdminMixin
|
||||
from .models import Keywords, Clusters, ContentIdeas
|
||||
from import_export.admin import ExportMixin
|
||||
from import_export import resources
|
||||
|
||||
|
||||
class KeywordsResource(resources.ModelResource):
|
||||
"""Resource class for exporting Keywords"""
|
||||
class Meta:
|
||||
model = Keywords
|
||||
fields = ('id', 'keyword', 'seed_keyword__keyword', 'site__name', 'sector__name',
|
||||
'cluster__name', 'volume', 'difficulty', 'intent', 'status', 'created_at')
|
||||
export_order = fields
|
||||
|
||||
|
||||
@admin.register(Clusters)
|
||||
@@ -9,6 +21,7 @@ class ClustersAdmin(SiteSectorAdminMixin, admin.ModelAdmin):
|
||||
list_filter = ['status', 'site', 'sector']
|
||||
search_fields = ['name']
|
||||
ordering = ['name']
|
||||
autocomplete_fields = ['site', 'sector']
|
||||
|
||||
def get_site_display(self, obj):
|
||||
"""Safely get site name"""
|
||||
@@ -27,11 +40,18 @@ class ClustersAdmin(SiteSectorAdminMixin, admin.ModelAdmin):
|
||||
|
||||
|
||||
@admin.register(Keywords)
|
||||
class KeywordsAdmin(SiteSectorAdminMixin, admin.ModelAdmin):
|
||||
class KeywordsAdmin(ExportMixin, SiteSectorAdminMixin, admin.ModelAdmin):
|
||||
resource_class = KeywordsResource
|
||||
list_display = ['keyword', 'seed_keyword', 'site', 'sector', 'cluster', 'volume', 'difficulty', 'intent', 'status', 'created_at']
|
||||
list_filter = ['status', 'seed_keyword__intent', 'site', 'sector', 'seed_keyword__industry', 'seed_keyword__sector']
|
||||
search_fields = ['seed_keyword__keyword']
|
||||
ordering = ['-created_at']
|
||||
autocomplete_fields = ['cluster', 'site', 'sector', 'seed_keyword']
|
||||
actions = [
|
||||
'bulk_assign_cluster',
|
||||
'bulk_set_status_active',
|
||||
'bulk_set_status_inactive',
|
||||
]
|
||||
|
||||
def get_site_display(self, obj):
|
||||
"""Safely get site name"""
|
||||
@@ -55,6 +75,58 @@ class KeywordsAdmin(SiteSectorAdminMixin, admin.ModelAdmin):
|
||||
except:
|
||||
return '-'
|
||||
get_cluster_display.short_description = 'Cluster'
|
||||
|
||||
def bulk_assign_cluster(self, request, queryset):
|
||||
"""Assign selected keywords to a cluster"""
|
||||
from django import forms
|
||||
|
||||
# If this is the POST request with cluster selection
|
||||
if 'apply' in request.POST:
|
||||
cluster_id = request.POST.get('cluster')
|
||||
if cluster_id:
|
||||
cluster = Clusters.objects.get(pk=cluster_id)
|
||||
updated = queryset.update(cluster=cluster)
|
||||
self.message_user(request, f'{updated} keyword(s) assigned to cluster: {cluster.name}', messages.SUCCESS)
|
||||
return
|
||||
|
||||
# Get first keyword's site/sector for filtering clusters
|
||||
first_keyword = queryset.first()
|
||||
if first_keyword:
|
||||
clusters = Clusters.objects.filter(site=first_keyword.site, sector=first_keyword.sector)
|
||||
else:
|
||||
clusters = Clusters.objects.all()
|
||||
|
||||
# Create form for cluster selection
|
||||
class ClusterForm(forms.Form):
|
||||
cluster = forms.ModelChoiceField(
|
||||
queryset=clusters,
|
||||
label="Select Cluster",
|
||||
help_text=f"Assign {queryset.count()} selected keyword(s) to:"
|
||||
)
|
||||
|
||||
if clusters.exists():
|
||||
from django.shortcuts import render
|
||||
return render(request, 'admin/bulk_action_form.html', {
|
||||
'title': 'Assign Keywords to Cluster',
|
||||
'queryset': queryset,
|
||||
'form': ClusterForm(),
|
||||
'action': 'bulk_assign_cluster',
|
||||
})
|
||||
else:
|
||||
self.message_user(request, 'No clusters available for the selected keywords.', messages.WARNING)
|
||||
bulk_assign_cluster.short_description = 'Assign to Cluster'
|
||||
|
||||
def bulk_set_status_active(self, request, queryset):
|
||||
"""Set selected keywords to active status"""
|
||||
updated = queryset.update(status='active')
|
||||
self.message_user(request, f'{updated} keyword(s) set to active.', messages.SUCCESS)
|
||||
bulk_set_status_active.short_description = 'Set status to Active'
|
||||
|
||||
def bulk_set_status_inactive(self, request, queryset):
|
||||
"""Set selected keywords to inactive status"""
|
||||
updated = queryset.update(status='inactive')
|
||||
self.message_user(request, f'{updated} keyword(s) set to inactive.', messages.SUCCESS)
|
||||
bulk_set_status_inactive.short_description = 'Set status to Inactive'
|
||||
|
||||
|
||||
@admin.register(ContentIdeas)
|
||||
|
||||
@@ -33,7 +33,13 @@ class TasksAdmin(ExportMixin, SiteSectorAdminMixin, admin.ModelAdmin):
|
||||
search_fields = ['title', 'description']
|
||||
ordering = ['-created_at']
|
||||
readonly_fields = ['created_at', 'updated_at']
|
||||
actions = ['bulk_set_status_draft', 'bulk_set_status_in_progress', 'bulk_set_status_completed']
|
||||
autocomplete_fields = ['cluster', 'site', 'sector']
|
||||
actions = [
|
||||
'bulk_set_status_draft',
|
||||
'bulk_set_status_in_progress',
|
||||
'bulk_set_status_completed',
|
||||
'bulk_assign_cluster',
|
||||
]
|
||||
|
||||
fieldsets = (
|
||||
('Basic Info', {
|
||||
@@ -69,6 +75,47 @@ class TasksAdmin(ExportMixin, SiteSectorAdminMixin, admin.ModelAdmin):
|
||||
self.message_user(request, f'{updated} task(s) set to completed.', messages.SUCCESS)
|
||||
bulk_set_status_completed.short_description = 'Set status to Completed'
|
||||
|
||||
def bulk_assign_cluster(self, request, queryset):
|
||||
"""Assign selected tasks to a cluster - requires form input"""
|
||||
from django import forms
|
||||
from igny8_core.modules.planner.models import Clusters
|
||||
|
||||
# If this is the POST request with cluster selection
|
||||
if 'apply' in request.POST:
|
||||
cluster_id = request.POST.get('cluster')
|
||||
if cluster_id:
|
||||
cluster = Clusters.objects.get(pk=cluster_id)
|
||||
updated = queryset.update(cluster=cluster)
|
||||
self.message_user(request, f'{updated} task(s) assigned to cluster: {cluster.name}', messages.SUCCESS)
|
||||
return
|
||||
|
||||
# Get first task's site/sector for filtering clusters
|
||||
first_task = queryset.first()
|
||||
if first_task:
|
||||
clusters = Clusters.objects.filter(site=first_task.site, sector=first_task.sector)
|
||||
else:
|
||||
clusters = Clusters.objects.all()
|
||||
|
||||
# Create form for cluster selection
|
||||
class ClusterForm(forms.Form):
|
||||
cluster = forms.ModelChoiceField(
|
||||
queryset=clusters,
|
||||
label="Select Cluster",
|
||||
help_text=f"Assign {queryset.count()} selected task(s) to:"
|
||||
)
|
||||
|
||||
if clusters.exists():
|
||||
from django.shortcuts import render
|
||||
return render(request, 'admin/bulk_action_form.html', {
|
||||
'title': 'Assign Tasks to Cluster',
|
||||
'queryset': queryset,
|
||||
'form': ClusterForm(),
|
||||
'action': 'bulk_assign_cluster',
|
||||
})
|
||||
else:
|
||||
self.message_user(request, 'No clusters available for the selected tasks.', messages.WARNING)
|
||||
bulk_assign_cluster.short_description = 'Assign to Cluster'
|
||||
|
||||
def get_site_display(self, obj):
|
||||
"""Safely get site name"""
|
||||
try:
|
||||
@@ -143,8 +190,13 @@ class ContentAdmin(ExportMixin, SiteSectorAdminMixin, admin.ModelAdmin):
|
||||
search_fields = ['title', 'content_html', 'external_url']
|
||||
ordering = ['-created_at']
|
||||
readonly_fields = ['created_at', 'updated_at', 'word_count', 'get_tags_display', 'get_categories_display']
|
||||
autocomplete_fields = ['cluster', 'site', 'sector']
|
||||
inlines = [ContentTaxonomyInline]
|
||||
actions = ['bulk_set_status_published', 'bulk_set_status_draft']
|
||||
actions = [
|
||||
'bulk_set_status_published',
|
||||
'bulk_set_status_draft',
|
||||
'bulk_add_taxonomy',
|
||||
]
|
||||
|
||||
fieldsets = (
|
||||
('Basic Info', {
|
||||
@@ -208,6 +260,55 @@ class ContentAdmin(ExportMixin, SiteSectorAdminMixin, admin.ModelAdmin):
|
||||
self.message_user(request, f'{updated} content item(s) set to draft.', messages.SUCCESS)
|
||||
bulk_set_status_draft.short_description = 'Set status to Draft'
|
||||
|
||||
def bulk_add_taxonomy(self, request, queryset):
|
||||
"""Add taxonomy terms to selected content"""
|
||||
from django import forms
|
||||
from igny8_core.business.content.models import ContentTaxonomy, ContentTaxonomyRelation
|
||||
|
||||
# If this is the POST request with taxonomy selection
|
||||
if 'apply' in request.POST:
|
||||
taxonomy_ids = request.POST.getlist('taxonomies')
|
||||
if taxonomy_ids:
|
||||
count = 0
|
||||
for content in queryset:
|
||||
for tax_id in taxonomy_ids:
|
||||
taxonomy = ContentTaxonomy.objects.get(pk=tax_id)
|
||||
ContentTaxonomyRelation.objects.get_or_create(
|
||||
content=content,
|
||||
taxonomy=taxonomy
|
||||
)
|
||||
count += 1
|
||||
self.message_user(request, f'Added {count} taxonomy relation(s) to {queryset.count()} content item(s).', messages.SUCCESS)
|
||||
return
|
||||
|
||||
# Get first content's site/sector for filtering taxonomies
|
||||
first_content = queryset.first()
|
||||
if first_content:
|
||||
taxonomies = ContentTaxonomy.objects.filter(site=first_content.site, sector=first_content.sector)
|
||||
else:
|
||||
taxonomies = ContentTaxonomy.objects.all()
|
||||
|
||||
# Create form for taxonomy selection
|
||||
class TaxonomyForm(forms.Form):
|
||||
taxonomies = forms.ModelMultipleChoiceField(
|
||||
queryset=taxonomies,
|
||||
label="Select Taxonomies",
|
||||
help_text=f"Add taxonomy terms to {queryset.count()} selected content item(s)",
|
||||
widget=forms.CheckboxSelectMultiple
|
||||
)
|
||||
|
||||
if taxonomies.exists():
|
||||
from django.shortcuts import render
|
||||
return render(request, 'admin/bulk_action_form.html', {
|
||||
'title': 'Add Taxonomies to Content',
|
||||
'queryset': queryset,
|
||||
'form': TaxonomyForm(),
|
||||
'action': 'bulk_add_taxonomy',
|
||||
})
|
||||
else:
|
||||
self.message_user(request, 'No taxonomies available for the selected content.', messages.WARNING)
|
||||
bulk_add_taxonomy.short_description = 'Add Taxonomy Terms'
|
||||
|
||||
def get_site_display(self, obj):
|
||||
"""Safely get site name"""
|
||||
try:
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
/* ===================================================================
|
||||
IGNY8 CUSTOM ADMIN STYLES
|
||||
IGNY8 CUSTOM ADMIN STYLES - COMPLETE REDESIGN
|
||||
===================================================================
|
||||
Using IGNY8 brand colors from frontend design system
|
||||
Using exact IGNY8 brand colors from frontend design system
|
||||
=================================================================== */
|
||||
|
||||
/* IGNY8 Brand Color Variables */
|
||||
/* IGNY8 Brand Color Variables - Matching Frontend App */
|
||||
:root {
|
||||
/* Primary Colors */
|
||||
--igny8-primary: #0693e3; /* Primary brand blue */
|
||||
--igny8-primary-dark: #0472b8; /* Primary dark */
|
||||
--igny8-primary-light: #3da9e8; /* Primary light */
|
||||
|
||||
/* Accent Colors */
|
||||
--igny8-success: #0bbf87; /* Success teal-green */
|
||||
--igny8-success-dark: #08966b; /* Success dark */
|
||||
--igny8-warning: #ff7a00; /* Warning orange */
|
||||
@@ -16,34 +20,560 @@
|
||||
--igny8-danger-dark: #d13333; /* Danger dark */
|
||||
--igny8-purple: #5d4ae3; /* Purple accent */
|
||||
--igny8-purple-dark: #3a2f94; /* Purple dark */
|
||||
|
||||
/* Neutral Colors */
|
||||
--igny8-navy: #0d1b2a; /* Dark navy background */
|
||||
--igny8-navy-light: #142b3f; /* Navy light */
|
||||
--igny8-navy-light: #1a2e44; /* Navy light */
|
||||
--igny8-surface: #f8fafc; /* Page background */
|
||||
--igny8-panel: #ffffff; /* Panel background */
|
||||
--igny8-text: #555a68; /* Main text */
|
||||
--igny8-text-dim: #64748b; /* Dimmed text */
|
||||
--igny8-text: #1e293b; /* Main text */
|
||||
--igny8-text-light: #64748b; /* Light text */
|
||||
--igny8-text-dim: #94a3b8; /* Dimmed text */
|
||||
--igny8-stroke: #e2e8f0; /* Borders */
|
||||
--igny8-stroke-dark: #cbd5e1; /* Dark borders */
|
||||
}
|
||||
|
||||
/* ===================================================================
|
||||
HEADER & BRANDING - IGNY8 Primary Blue with Gradient
|
||||
GLOBAL RESETS
|
||||
=================================================================== */
|
||||
body {
|
||||
background: var(--igny8-surface) !important;
|
||||
color: var(--igny8-text) !important;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !important;
|
||||
}
|
||||
|
||||
/* ===================================================================
|
||||
HEADER - Clean Professional Design
|
||||
=================================================================== */
|
||||
#header {
|
||||
background: linear-gradient(135deg, var(--igny8-primary) 0%, var(--igny8-primary-dark) 100%) !important;
|
||||
color: white !important;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1) !important;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.1) !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
#header a:link, #header a:visited {
|
||||
#branding {
|
||||
padding: 16px 30px !important;
|
||||
}
|
||||
|
||||
#branding h1 {
|
||||
margin: 0 !important;
|
||||
font-size: 20px !important;
|
||||
font-weight: 600 !important;
|
||||
}
|
||||
|
||||
#branding h1 a:link,
|
||||
#branding h1 a:visited {
|
||||
color: white !important;
|
||||
text-decoration: none !important;
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
gap: 10px !important;
|
||||
}
|
||||
|
||||
#header a:link,
|
||||
#header a:visited {
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
#branding h1, #branding h1 a:link, #branding h1 a:visited {
|
||||
color: white !important;
|
||||
/* ===================================================================
|
||||
ADD RECORD BUTTON - Modern Accent Color
|
||||
=================================================================== */
|
||||
.object-tools {
|
||||
margin-bottom: 24px !important;
|
||||
float: right !important;
|
||||
}
|
||||
|
||||
.header-user-tools a {
|
||||
.object-tools li {
|
||||
margin: 0 !important;
|
||||
list-style: none !important;
|
||||
}
|
||||
|
||||
.object-tools a.addlink {
|
||||
background: linear-gradient(135deg, var(--igny8-success) 0%, var(--igny8-success-dark) 100%) !important;
|
||||
color: white !important;
|
||||
padding: 12px 28px !important;
|
||||
border-radius: 8px !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 14px !important;
|
||||
text-decoration: none !important;
|
||||
display: inline-flex !important;
|
||||
align-items: center !important;
|
||||
gap: 10px !important;
|
||||
box-shadow: 0 2px 6px rgba(11, 191, 135, 0.3) !important;
|
||||
transition: all 0.2s ease !important;
|
||||
border: none !important;
|
||||
text-transform: none !important;
|
||||
}
|
||||
|
||||
.object-tools a.addlink:before {
|
||||
content: "\f067" !important;
|
||||
font-family: "Font Awesome 6 Free" !important;
|
||||
font-weight: 900 !important;
|
||||
font-size: 14px !important;
|
||||
margin-right: 0 !important;
|
||||
background: none !important;
|
||||
border: none !important;
|
||||
width: auto !important;
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
.object-tools a.addlink:hover {
|
||||
background: linear-gradient(135deg, var(--igny8-success-dark) 0%, #067354 100%) !important;
|
||||
box-shadow: 0 4px 12px rgba(11, 191, 135, 0.4) !important;
|
||||
transform: translateY(-2px) !important;
|
||||
}
|
||||
|
||||
/* ===================================================================
|
||||
EXPORT BUTTON - Purple Accent
|
||||
=================================================================== */
|
||||
a[href*="export"],
|
||||
.export-button,
|
||||
input[name="_export"] {
|
||||
background: linear-gradient(135deg, var(--igny8-purple) 0%, var(--igny8-purple-dark) 100%) !important;
|
||||
color: white !important;
|
||||
padding: 10px 24px !important;
|
||||
border-radius: 8px !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 13px !important;
|
||||
text-decoration: none !important;
|
||||
display: inline-flex !important;
|
||||
align-items: center !important;
|
||||
gap: 8px !important;
|
||||
box-shadow: 0 2px 6px rgba(93, 74, 227, 0.3) !important;
|
||||
transition: all 0.2s ease !important;
|
||||
border: none !important;
|
||||
cursor: pointer !important;
|
||||
}
|
||||
|
||||
a[href*="export"]:hover,
|
||||
.export-button:hover,
|
||||
input[name="_export"]:hover {
|
||||
background: linear-gradient(135deg, var(--igny8-purple-dark) 0%, #2a1f6b 100%) !important;
|
||||
box-shadow: 0 4px 12px rgba(93, 74, 227, 0.4) !important;
|
||||
transform: translateY(-2px) !important;
|
||||
}
|
||||
|
||||
/* ===================================================================
|
||||
SEARCH BAR & TOOLBAR - Professional Layout
|
||||
=================================================================== */
|
||||
#toolbar {
|
||||
padding: 20px 24px !important;
|
||||
background: var(--igny8-panel) !important;
|
||||
border: 1px solid var(--igny8-stroke) !important;
|
||||
border-radius: 12px !important;
|
||||
margin-bottom: 24px !important;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.05) !important;
|
||||
}
|
||||
|
||||
#toolbar form {
|
||||
display: flex !important;
|
||||
gap: 12px !important;
|
||||
align-items: center !important;
|
||||
}
|
||||
|
||||
#toolbar input[type="text"],
|
||||
#searchbar {
|
||||
flex: 1 !important;
|
||||
min-width: 320px !important;
|
||||
max-width: 500px !important;
|
||||
padding: 12px 18px !important;
|
||||
border: 1.5px solid var(--igny8-stroke) !important;
|
||||
border-radius: 8px !important;
|
||||
font-size: 14px !important;
|
||||
transition: all 0.2s ease !important;
|
||||
background: var(--igny8-panel) !important;
|
||||
color: var(--igny8-text) !important;
|
||||
}
|
||||
|
||||
#toolbar input[type="text"]:focus,
|
||||
#searchbar:focus {
|
||||
border-color: var(--igny8-primary) !important;
|
||||
outline: none !important;
|
||||
box-shadow: 0 0 0 4px rgba(6, 147, 227, 0.1) !important;
|
||||
}
|
||||
|
||||
#toolbar input[type="submit"],
|
||||
#toolbar button[type="submit"] {
|
||||
background: linear-gradient(135deg, var(--igny8-primary) 0%, var(--igny8-primary-dark) 100%) !important;
|
||||
color: white !important;
|
||||
padding: 12px 28px !important;
|
||||
border: none !important;
|
||||
border-radius: 8px !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 14px !important;
|
||||
cursor: pointer !important;
|
||||
transition: all 0.2s ease !important;
|
||||
box-shadow: 0 2px 6px rgba(6, 147, 227, 0.3) !important;
|
||||
}
|
||||
|
||||
#toolbar input[type="submit"]:hover,
|
||||
#toolbar button[type="submit"]:hover {
|
||||
background: linear-gradient(135deg, var(--igny8-primary-dark) 0%, #035a8f 100%) !important;
|
||||
box-shadow: 0 4px 12px rgba(6, 147, 227, 0.4) !important;
|
||||
transform: translateY(-2px) !important;
|
||||
}
|
||||
|
||||
/* ===================================================================
|
||||
ACTIONS BAR - Better Styling
|
||||
=================================================================== */
|
||||
#changelist-form .actions {
|
||||
padding: 20px 24px !important;
|
||||
background: var(--igny8-panel) !important;
|
||||
border: 1px solid var(--igny8-stroke) !important;
|
||||
border-radius: 12px !important;
|
||||
margin-bottom: 24px !important;
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
gap: 16px !important;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.05) !important;
|
||||
}
|
||||
|
||||
#changelist-form .actions label {
|
||||
font-weight: 600 !important;
|
||||
color: var(--igny8-text) !important;
|
||||
font-size: 14px !important;
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
#changelist-form .actions select {
|
||||
min-width: 240px !important;
|
||||
padding: 12px 18px !important;
|
||||
border: 1.5px solid var(--igny8-stroke) !important;
|
||||
border-radius: 8px !important;
|
||||
font-size: 14px !important;
|
||||
transition: all 0.2s ease !important;
|
||||
background: var(--igny8-panel) !important;
|
||||
color: var(--igny8-text) !important;
|
||||
}
|
||||
|
||||
#changelist-form .actions select:focus {
|
||||
border-color: var(--igny8-primary) !important;
|
||||
outline: none !important;
|
||||
box-shadow: 0 0 0 4px rgba(6, 147, 227, 0.1) !important;
|
||||
}
|
||||
|
||||
#changelist-form .actions button,
|
||||
#changelist-form .actions input[type="submit"] {
|
||||
background: linear-gradient(135deg, var(--igny8-primary) 0%, var(--igny8-primary-dark) 100%) !important;
|
||||
color: white !important;
|
||||
padding: 12px 28px !important;
|
||||
border: none !important;
|
||||
border-radius: 8px !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 14px !important;
|
||||
cursor: pointer !important;
|
||||
transition: all 0.2s ease !important;
|
||||
box-shadow: 0 2px 6px rgba(6, 147, 227, 0.3) !important;
|
||||
}
|
||||
|
||||
#changelist-form .actions button:hover,
|
||||
#changelist-form .actions input[type="submit"]:hover {
|
||||
background: linear-gradient(135deg, var(--igny8-primary-dark) 0%, #035a8f 100%) !important;
|
||||
box-shadow: 0 4px 12px rgba(6, 147, 227, 0.4) !important;
|
||||
transform: translateY(-2px) !important;
|
||||
}
|
||||
|
||||
/* ===================================================================
|
||||
FILTERS PANEL - Clear Organization
|
||||
=================================================================== */
|
||||
#changelist-filter {
|
||||
background: var(--igny8-panel) !important;
|
||||
border: 1px solid var(--igny8-stroke) !important;
|
||||
border-radius: 12px !important;
|
||||
padding: 0 !important;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.05) !important;
|
||||
}
|
||||
|
||||
#changelist-filter h2 {
|
||||
background: linear-gradient(135deg, var(--igny8-navy) 0%, var(--igny8-navy-light) 100%) !important;
|
||||
color: white !important;
|
||||
padding: 18px 24px !important;
|
||||
margin: 0 !important;
|
||||
font-size: 16px !important;
|
||||
font-weight: 600 !important;
|
||||
border-radius: 12px 12px 0 0 !important;
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
gap: 10px !important;
|
||||
}
|
||||
|
||||
#changelist-filter h2:before {
|
||||
content: "\f0b0" !important;
|
||||
font-family: "Font Awesome 6 Free" !important;
|
||||
font-weight: 900 !important;
|
||||
}
|
||||
|
||||
#changelist-filter h3 {
|
||||
background: var(--igny8-surface) !important;
|
||||
color: var(--igny8-text) !important;
|
||||
padding: 14px 24px !important;
|
||||
margin: 0 !important;
|
||||
font-size: 14px !important;
|
||||
font-weight: 600 !important;
|
||||
border-top: 1px solid var(--igny8-stroke) !important;
|
||||
border-bottom: 1px solid var(--igny8-stroke) !important;
|
||||
}
|
||||
|
||||
#changelist-filter ul {
|
||||
padding: 16px 24px !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
#changelist-filter li {
|
||||
padding: 0 !important;
|
||||
margin: 0 0 8px 0 !important;
|
||||
list-style: none !important;
|
||||
}
|
||||
|
||||
#changelist-filter a {
|
||||
color: var(--igny8-text) !important;
|
||||
text-decoration: none !important;
|
||||
display: block !important;
|
||||
padding: 10px 16px !important;
|
||||
border-radius: 6px !important;
|
||||
transition: all 0.2s ease !important;
|
||||
font-size: 14px !important;
|
||||
}
|
||||
|
||||
#changelist-filter a:hover {
|
||||
background: var(--igny8-surface) !important;
|
||||
color: var(--igny8-primary) !important;
|
||||
}
|
||||
|
||||
#changelist-filter a.selected {
|
||||
background: linear-gradient(135deg, var(--igny8-primary) 0%, var(--igny8-primary-dark) 100%) !important;
|
||||
color: white !important;
|
||||
font-weight: 600 !important;
|
||||
box-shadow: 0 2px 4px rgba(6, 147, 227, 0.2) !important;
|
||||
}
|
||||
|
||||
#changelist-filter .quiet {
|
||||
color: var(--igny8-text-dim) !important;
|
||||
font-size: 13px !important;
|
||||
}
|
||||
|
||||
/* ===================================================================
|
||||
ADD RECORD BUTTON - Clean Professional Style
|
||||
=================================================================== */
|
||||
.object-tools {
|
||||
margin-bottom: 20px !important;
|
||||
}
|
||||
|
||||
.object-tools li {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.object-tools a.addlink {
|
||||
background: linear-gradient(135deg, var(--igny8-primary) 0%, var(--igny8-primary-dark) 100%) !important;
|
||||
color: white !important;
|
||||
padding: 12px 24px !important;
|
||||
border-radius: 8px !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 14px !important;
|
||||
text-decoration: none !important;
|
||||
display: inline-flex !important;
|
||||
align-items: center !important;
|
||||
gap: 8px !important;
|
||||
box-shadow: 0 2px 4px rgba(6, 147, 227, 0.3) !important;
|
||||
transition: all 0.2s ease !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.object-tools a.addlink:before {
|
||||
content: "+" !important;
|
||||
font-size: 18px !important;
|
||||
font-weight: bold !important;
|
||||
margin-right: 0 !important;
|
||||
background: none !important;
|
||||
border: none !important;
|
||||
width: auto !important;
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
.object-tools a.addlink:hover {
|
||||
background: linear-gradient(135deg, var(--igny8-primary-dark) 0%, #035a8f 100%) !important;
|
||||
box-shadow: 0 4px 8px rgba(6, 147, 227, 0.4) !important;
|
||||
transform: translateY(-1px) !important;
|
||||
}
|
||||
|
||||
/* Remove the background from the + icon */
|
||||
.object-tools a.addlink:before {
|
||||
background: none !important;
|
||||
}
|
||||
|
||||
/* ===================================================================
|
||||
SEARCH & ACTION BAR - Better Sizing and Layout
|
||||
=================================================================== */
|
||||
#toolbar {
|
||||
padding: 16px 20px !important;
|
||||
background: var(--igny8-panel) !important;
|
||||
border: 1px solid var(--igny8-stroke) !important;
|
||||
border-radius: 8px !important;
|
||||
margin-bottom: 20px !important;
|
||||
display: flex !important;
|
||||
gap: 16px !important;
|
||||
align-items: center !important;
|
||||
flex-wrap: wrap !important;
|
||||
}
|
||||
|
||||
#toolbar form {
|
||||
display: flex !important;
|
||||
gap: 12px !important;
|
||||
align-items: center !important;
|
||||
flex: 1 !important;
|
||||
max-width: 600px !important;
|
||||
}
|
||||
|
||||
#toolbar input[type="text"] {
|
||||
flex: 1 !important;
|
||||
min-width: 250px !important;
|
||||
padding: 10px 16px !important;
|
||||
border: 1px solid var(--igny8-stroke) !important;
|
||||
border-radius: 6px !important;
|
||||
font-size: 14px !important;
|
||||
transition: all 0.2s ease !important;
|
||||
}
|
||||
|
||||
#toolbar input[type="text"]:focus {
|
||||
border-color: var(--igny8-primary) !important;
|
||||
outline: none !important;
|
||||
box-shadow: 0 0 0 3px rgba(6, 147, 227, 0.1) !important;
|
||||
}
|
||||
|
||||
#toolbar input[type="submit"],
|
||||
#toolbar button[type="submit"] {
|
||||
background: linear-gradient(135deg, var(--igny8-primary) 0%, var(--igny8-primary-dark) 100%) !important;
|
||||
color: white !important;
|
||||
padding: 10px 24px !important;
|
||||
border: none !important;
|
||||
border-radius: 6px !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 14px !important;
|
||||
cursor: pointer !important;
|
||||
transition: all 0.2s ease !important;
|
||||
box-shadow: 0 2px 4px rgba(6, 147, 227, 0.2) !important;
|
||||
}
|
||||
|
||||
#toolbar input[type="submit"]:hover,
|
||||
#toolbar button[type="submit"]:hover {
|
||||
background: linear-gradient(135deg, var(--igny8-primary-dark) 0%, #035a8f 100%) !important;
|
||||
box-shadow: 0 4px 8px rgba(6, 147, 227, 0.3) !important;
|
||||
transform: translateY(-1px) !important;
|
||||
}
|
||||
|
||||
/* Action dropdown */
|
||||
#changelist-form .actions {
|
||||
padding: 16px 20px !important;
|
||||
background: var(--igny8-panel) !important;
|
||||
border: 1px solid var(--igny8-stroke) !important;
|
||||
border-radius: 8px !important;
|
||||
margin-bottom: 20px !important;
|
||||
}
|
||||
|
||||
#changelist-form .actions select {
|
||||
min-width: 220px !important;
|
||||
padding: 10px 16px !important;
|
||||
border: 1px solid var(--igny8-stroke) !important;
|
||||
border-radius: 6px !important;
|
||||
font-size: 14px !important;
|
||||
margin-right: 12px !important;
|
||||
transition: all 0.2s ease !important;
|
||||
}
|
||||
|
||||
#changelist-form .actions select:focus {
|
||||
border-color: var(--igny8-primary) !important;
|
||||
outline: none !important;
|
||||
box-shadow: 0 0 0 3px rgba(6, 147, 227, 0.1) !important;
|
||||
}
|
||||
|
||||
#changelist-form .actions button,
|
||||
#changelist-form .actions input[type="submit"] {
|
||||
background: linear-gradient(135deg, var(--igny8-primary) 0%, var(--igny8-primary-dark) 100%) !important;
|
||||
color: white !important;
|
||||
padding: 10px 24px !important;
|
||||
border: none !important;
|
||||
border-radius: 6px !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 14px !important;
|
||||
cursor: pointer !important;
|
||||
transition: all 0.2s ease !important;
|
||||
box-shadow: 0 2px 4px rgba(6, 147, 227, 0.2) !important;
|
||||
}
|
||||
|
||||
#changelist-form .actions button:hover,
|
||||
#changelist-form .actions input[type="submit"]:hover {
|
||||
background: linear-gradient(135deg, var(--igny8-primary-dark) 0%, #035a8f 100%) !important;
|
||||
box-shadow: 0 4px 8px rgba(6, 147, 227, 0.3) !important;
|
||||
transform: translateY(-1px) !important;
|
||||
}
|
||||
|
||||
/* ===================================================================
|
||||
FILTERS - Clear Labels and Better Organization
|
||||
=================================================================== */
|
||||
#changelist-filter {
|
||||
background: var(--igny8-panel) !important;
|
||||
border: 1px solid var(--igny8-stroke) !important;
|
||||
border-radius: 8px !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
#changelist-filter h2 {
|
||||
background: linear-gradient(135deg, var(--igny8-navy) 0%, var(--igny8-navy-light) 100%) !important;
|
||||
color: white !important;
|
||||
padding: 16px 20px !important;
|
||||
margin: 0 !important;
|
||||
font-size: 16px !important;
|
||||
font-weight: 600 !important;
|
||||
border-radius: 8px 8px 0 0 !important;
|
||||
}
|
||||
|
||||
#changelist-filter h3 {
|
||||
background: var(--igny8-surface) !important;
|
||||
color: var(--igny8-text) !important;
|
||||
padding: 12px 20px !important;
|
||||
margin: 0 !important;
|
||||
font-size: 14px !important;
|
||||
font-weight: 600 !important;
|
||||
border-top: 1px solid var(--igny8-stroke) !important;
|
||||
border-bottom: 1px solid var(--igny8-stroke) !important;
|
||||
}
|
||||
|
||||
#changelist-filter ul {
|
||||
padding: 12px 20px !important;
|
||||
margin: 0 0 16px 0 !important;
|
||||
}
|
||||
|
||||
#changelist-filter li {
|
||||
padding: 8px 0 !important;
|
||||
margin: 0 !important;
|
||||
list-style: none !important;
|
||||
}
|
||||
|
||||
#changelist-filter a {
|
||||
color: var(--igny8-text) !important;
|
||||
text-decoration: none !important;
|
||||
display: block !important;
|
||||
padding: 6px 12px !important;
|
||||
border-radius: 4px !important;
|
||||
transition: all 0.2s ease !important;
|
||||
font-size: 14px !important;
|
||||
}
|
||||
|
||||
#changelist-filter a:hover {
|
||||
background: var(--igny8-surface) !important;
|
||||
color: var(--igny8-primary) !important;
|
||||
}
|
||||
|
||||
#changelist-filter a.selected {
|
||||
background: linear-gradient(135deg, var(--igny8-primary) 0%, var(--igny8-primary-dark) 100%) !important;
|
||||
color: white !important;
|
||||
font-weight: 600 !important;
|
||||
}
|
||||
|
||||
#changelist-filter .quiet {
|
||||
color: var(--igny8-text-dim) !important;
|
||||
font-size: 13px !important;
|
||||
}
|
||||
|
||||
/* ===================================================================
|
||||
@@ -271,6 +801,217 @@
|
||||
background: var(--igny8-danger-dark) !important;
|
||||
}
|
||||
|
||||
/* ===================================================================
|
||||
SIDEBAR MODULE "ADD" LINKS - Icon Only with Theme Colors
|
||||
=================================================================== */
|
||||
.module .addlink,
|
||||
.module a.addlink,
|
||||
#content-main .module .addlink {
|
||||
color: var(--igny8-text) !important;
|
||||
text-decoration: none !important;
|
||||
font-size: 0 !important;
|
||||
padding: 0 !important;
|
||||
display: inline-flex !important;
|
||||
align-items: center !important;
|
||||
justify-content: center !important;
|
||||
transition: all 0.2s ease !important;
|
||||
}
|
||||
|
||||
.module .addlink:before,
|
||||
.module a.addlink:before {
|
||||
content: "\f067" !important;
|
||||
font-family: "Font Awesome 6 Free" !important;
|
||||
font-weight: 900 !important;
|
||||
font-size: 12px !important;
|
||||
display: inline-flex !important;
|
||||
align-items: center !important;
|
||||
justify-content: center !important;
|
||||
width: 24px !important;
|
||||
height: 24px !important;
|
||||
border-radius: 6px !important;
|
||||
background: linear-gradient(135deg, var(--igny8-success) 0%, var(--igny8-success-dark) 100%) !important;
|
||||
color: white !important;
|
||||
margin: 0 !important;
|
||||
transition: all 0.2s ease !important;
|
||||
box-shadow: 0 2px 4px rgba(11, 191, 135, 0.2) !important;
|
||||
}
|
||||
|
||||
.module .addlink:hover:before,
|
||||
.module a.addlink:hover:before {
|
||||
background: linear-gradient(135deg, var(--igny8-success-dark) 0%, #067354 100%) !important;
|
||||
box-shadow: 0 4px 8px rgba(11, 191, 135, 0.3) !important;
|
||||
transform: translateY(-1px) scale(1.05) !important;
|
||||
}
|
||||
|
||||
/* Module navigation styling */
|
||||
.module {
|
||||
margin-bottom: 20px !important;
|
||||
background: var(--igny8-panel) !important;
|
||||
border: 1px solid var(--igny8-stroke) !important;
|
||||
border-radius: 8px !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
.module h2 {
|
||||
background: linear-gradient(135deg, var(--igny8-navy) 0%, var(--igny8-navy-light) 100%) !important;
|
||||
color: white !important;
|
||||
padding: 14px 16px !important;
|
||||
font-size: 14px !important;
|
||||
font-weight: 600 !important;
|
||||
margin: 0 !important;
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
gap: 10px !important;
|
||||
}
|
||||
|
||||
.module table {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.module tr {
|
||||
border-bottom: 1px solid var(--igny8-stroke) !important;
|
||||
}
|
||||
|
||||
.module tr:last-child {
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
||||
.module th,
|
||||
.module td {
|
||||
padding: 12px 16px !important;
|
||||
text-align: left !important;
|
||||
}
|
||||
|
||||
.module th {
|
||||
background: var(--igny8-surface) !important;
|
||||
color: var(--igny8-text) !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 13px !important;
|
||||
}
|
||||
|
||||
.module a {
|
||||
color: var(--igny8-primary) !important;
|
||||
text-decoration: none !important;
|
||||
transition: color 0.2s ease !important;
|
||||
}
|
||||
|
||||
.module a:hover {
|
||||
color: var(--igny8-primary-dark) !important;
|
||||
}
|
||||
|
||||
/* ===================================================================
|
||||
ALL BUTTONS - Consistent Theme Colors
|
||||
=================================================================== */
|
||||
.button,
|
||||
input[type=submit],
|
||||
input[type=button],
|
||||
.submit-row input,
|
||||
button:not(.close) {
|
||||
background: linear-gradient(135deg, var(--igny8-primary) 0%, var(--igny8-primary-dark) 100%) !important;
|
||||
color: white !important;
|
||||
border: none !important;
|
||||
padding: 12px 24px !important;
|
||||
border-radius: 8px !important;
|
||||
cursor: pointer !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 14px !important;
|
||||
transition: all 0.2s ease !important;
|
||||
box-shadow: 0 2px 6px rgba(6, 147, 227, 0.3) !important;
|
||||
}
|
||||
|
||||
.button:hover,
|
||||
input[type=submit]:hover,
|
||||
input[type=button]:hover,
|
||||
button:not(.close):hover {
|
||||
background: linear-gradient(135deg, var(--igny8-primary-dark) 0%, #035a8f 100%) !important;
|
||||
box-shadow: 0 4px 12px rgba(6, 147, 227, 0.4) !important;
|
||||
transform: translateY(-2px) !important;
|
||||
}
|
||||
|
||||
/* Save buttons - Success color */
|
||||
input[name="_save"],
|
||||
input[name="_continue"],
|
||||
.submit-row input[type="submit"]:first-child {
|
||||
background: linear-gradient(135deg, var(--igny8-success) 0%, var(--igny8-success-dark) 100%) !important;
|
||||
box-shadow: 0 2px 6px rgba(11, 191, 135, 0.3) !important;
|
||||
}
|
||||
|
||||
input[name="_save"]:hover,
|
||||
input[name="_continue"]:hover {
|
||||
background: linear-gradient(135deg, var(--igny8-success-dark) 0%, #067354 100%) !important;
|
||||
box-shadow: 0 4px 12px rgba(11, 191, 135, 0.4) !important;
|
||||
}
|
||||
|
||||
/* Delete buttons - Danger color */
|
||||
.deletelink,
|
||||
.deletelink-box a,
|
||||
a.deletelink:link,
|
||||
a.deletelink:visited,
|
||||
input[name="_delete"],
|
||||
.delete-confirmation input[type="submit"] {
|
||||
background: linear-gradient(135deg, var(--igny8-danger) 0%, var(--igny8-danger-dark) 100%) !important;
|
||||
color: white !important;
|
||||
box-shadow: 0 2px 6px rgba(239, 68, 68, 0.3) !important;
|
||||
}
|
||||
|
||||
.deletelink:hover,
|
||||
.deletelink-box a:hover,
|
||||
input[name="_delete"]:hover {
|
||||
background: linear-gradient(135deg, var(--igny8-danger-dark) 0%, #b82222 100%) !important;
|
||||
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.4) !important;
|
||||
}
|
||||
|
||||
/* ===================================================================
|
||||
TABLE IMPROVEMENTS
|
||||
=================================================================== */
|
||||
#result_list {
|
||||
border: 1px solid var(--igny8-stroke) !important;
|
||||
border-radius: 12px !important;
|
||||
overflow: hidden !important;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.05) !important;
|
||||
}
|
||||
|
||||
#result_list thead th {
|
||||
background: linear-gradient(135deg, var(--igny8-surface) 0%, #f1f5f9 100%) !important;
|
||||
color: var(--igny8-text) !important;
|
||||
padding: 16px 12px !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 13px !important;
|
||||
text-transform: uppercase !important;
|
||||
letter-spacing: 0.5px !important;
|
||||
border-bottom: 2px solid var(--igny8-stroke-dark) !important;
|
||||
}
|
||||
|
||||
#result_list thead th a {
|
||||
color: var(--igny8-text) !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
#result_list tbody tr {
|
||||
transition: background-color 0.2s ease !important;
|
||||
}
|
||||
|
||||
#result_list tbody tr:nth-child(odd) {
|
||||
background-color: white !important;
|
||||
}
|
||||
|
||||
#result_list tbody tr:nth-child(even) {
|
||||
background-color: var(--igny8-surface) !important;
|
||||
}
|
||||
|
||||
#result_list tbody tr:hover {
|
||||
background-color: rgba(6, 147, 227, 0.05) !important;
|
||||
}
|
||||
|
||||
#result_list td {
|
||||
padding: 14px 12px !important;
|
||||
color: var(--igny8-text) !important;
|
||||
font-size: 14px !important;
|
||||
border-bottom: 1px solid var(--igny8-stroke) !important;
|
||||
}
|
||||
background: var(--igny8-danger-dark) !important;
|
||||
}
|
||||
|
||||
/* ===================================================================
|
||||
LINKS - IGNY8 Primary Blue
|
||||
=================================================================== */
|
||||
@@ -517,3 +1258,46 @@ fieldset.module h2 {
|
||||
border-left-color: var(--igny8-success);
|
||||
color: var(--igny8-success-dark);
|
||||
}
|
||||
|
||||
/* ===================================================================
|
||||
SIDEBAR MODULE ADD LINKS - Clean and Professional
|
||||
=================================================================== */
|
||||
.module .addlink,
|
||||
.module a.addlink,
|
||||
#content-main .module .addlink {
|
||||
color: var(--igny8-primary) !important;
|
||||
text-decoration: none !important;
|
||||
font-size: 13px !important;
|
||||
font-weight: 500 !important;
|
||||
padding: 6px 10px !important;
|
||||
display: inline-block !important;
|
||||
border-radius: 4px !important;
|
||||
transition: all 0.2s ease !important;
|
||||
}
|
||||
|
||||
.module .addlink:before,
|
||||
.module a.addlink:before {
|
||||
content: "+" !important;
|
||||
display: inline-block !important;
|
||||
width: 18px !important;
|
||||
height: 18px !important;
|
||||
line-height: 18px !important;
|
||||
text-align: center !important;
|
||||
border-radius: 3px !important;
|
||||
background: var(--igny8-primary) !important;
|
||||
color: white !important;
|
||||
margin-right: 6px !important;
|
||||
font-size: 14px !important;
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.module .addlink:hover,
|
||||
.module a.addlink:hover {
|
||||
background: rgba(6, 147, 227, 0.1) !important;
|
||||
color: var(--igny8-primary-dark) !important;
|
||||
}
|
||||
|
||||
.module .addlink:hover:before,
|
||||
.module a.addlink:hover:before {
|
||||
background: var(--igny8-primary-dark) !important;
|
||||
}
|
||||
|
||||
@@ -3,17 +3,68 @@
|
||||
|
||||
{% block title %}{{ title }} | IGNY8 Admin{% endblock %}
|
||||
|
||||
{% block extrahead %}
|
||||
{{ block.super }}
|
||||
<!-- FontAwesome Icons -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" integrity="sha512-DTOQO9RWCH3ppGqcWaEA1BIZOC6xxalwEsw9c2QQeAIftl+Vegovlnee1c9QX4TctnWMn13TZye+giMm8e2LwA==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
||||
{% endblock %}
|
||||
|
||||
{% block branding %}
|
||||
<h1 id="site-name">
|
||||
<a href="{% url 'admin:index' %}">
|
||||
🚀 IGNY8 Administration
|
||||
<i class="fas fa-rocket"></i> IGNY8 Administration
|
||||
</a>
|
||||
</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block userlinks %}
|
||||
<a href="{% url 'admin:dashboard' %}" class="dashboard-link">
|
||||
<i class="fas fa-chart-line"></i>
|
||||
<span>Dashboard</span>
|
||||
</a>
|
||||
{{ block.super }}
|
||||
{% endblock %}
|
||||
|
||||
{% block extrastyle %}
|
||||
{{ block.super }}
|
||||
<link rel="stylesheet" href="{% static 'admin/css/igny8_admin.css' %}">
|
||||
<style>
|
||||
/* Dashboard link in header */
|
||||
.dashboard-link {
|
||||
display: inline-flex !important;
|
||||
align-items: center !important;
|
||||
gap: 8px !important;
|
||||
padding: 10px 20px !important;
|
||||
margin-right: 20px !important;
|
||||
background: rgba(255, 255, 255, 0.15) !important;
|
||||
color: white !important;
|
||||
text-decoration: none !important;
|
||||
border-radius: 8px !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 14px !important;
|
||||
transition: all 0.2s ease !important;
|
||||
backdrop-filter: blur(10px) !important;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2) !important;
|
||||
}
|
||||
|
||||
.dashboard-link:hover {
|
||||
background: rgba(255, 255, 255, 0.25) !important;
|
||||
transform: translateY(-1px) !important;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15) !important;
|
||||
}
|
||||
|
||||
.dashboard-link i {
|
||||
font-size: 16px !important;
|
||||
}
|
||||
|
||||
/* User tools spacing */
|
||||
#user-tools {
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
gap: 15px !important;
|
||||
padding: 10px 20px !important;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block nav-global %}{% endblock %}
|
||||
|
||||
75
backend/igny8_core/templates/admin/bulk_action_form.html
Normal file
75
backend/igny8_core/templates/admin/bulk_action_form.html
Normal file
@@ -0,0 +1,75 @@
|
||||
{% extends "admin/base_site.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{{ title }}</h1>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
|
||||
<div class="form-group" style="margin: 20px 0;">
|
||||
{{ form.as_p }}
|
||||
</div>
|
||||
|
||||
<div class="submit-row" style="margin-top: 20px;">
|
||||
<input type="hidden" name="action" value="{{ action }}">
|
||||
<input type="hidden" name="_selected_action" value="{{ queryset|join:',' }}">
|
||||
<input type="submit" name="apply" value="Apply" class="button" style="margin-right: 10px;">
|
||||
<a href="javascript:history.back()" class="button">Cancel</a>
|
||||
</div>
|
||||
|
||||
<fieldset class="module aligned" style="margin-top: 20px;">
|
||||
<h2>Selected Items ({{ queryset.count }})</h2>
|
||||
<ul style="list-style: none; padding: 10px;">
|
||||
{% for item in queryset|slice:":10" %}
|
||||
<li style="padding: 5px 0; border-bottom: 1px solid var(--igny8-stroke);">
|
||||
{{ item }}
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% if queryset.count > 10 %}
|
||||
<li style="padding: 10px 0; font-style: italic; color: var(--igny8-text-dim);">
|
||||
...and {{ queryset.count|add:"-10" }} more
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
<style>
|
||||
.form-group label {
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
color: var(--igny8-text);
|
||||
}
|
||||
|
||||
.form-group select,
|
||||
.form-group input {
|
||||
padding: 8px;
|
||||
border: 1px solid var(--igny8-stroke);
|
||||
border-radius: 4px;
|
||||
min-width: 300px;
|
||||
}
|
||||
|
||||
.form-group .helptext {
|
||||
display: block;
|
||||
margin-top: 5px;
|
||||
color: var(--igny8-text-dim);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.form-group ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.form-group ul li {
|
||||
padding: 5px 0;
|
||||
}
|
||||
|
||||
.form-group ul li label {
|
||||
font-weight: normal;
|
||||
margin-left: 5px;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
419
backend/igny8_core/templates/admin/dashboard.html
Normal file
419
backend/igny8_core/templates/admin/dashboard.html
Normal file
@@ -0,0 +1,419 @@
|
||||
{% extends "admin/base_site.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}IGNY8 Dashboard{% endblock %}
|
||||
|
||||
{% block extrahead %}
|
||||
{{ block.super }}
|
||||
<style>
|
||||
.dashboard-container {
|
||||
padding: 20px;
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.dashboard-header {
|
||||
background: linear-gradient(135deg, #0d1b2a 0%, #1a2e44 100%);
|
||||
color: white;
|
||||
padding: 30px;
|
||||
border-radius: 12px;
|
||||
margin-bottom: 30px;
|
||||
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.dashboard-header-content h1 {
|
||||
margin: 0 0 10px 0;
|
||||
font-size: 32px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.dashboard-header-content p {
|
||||
margin: 0;
|
||||
opacity: 0.9;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.dashboard-nav {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.dashboard-nav a {
|
||||
padding: 10px 20px;
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
color: white !important;
|
||||
text-decoration: none;
|
||||
border-radius: 6px;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
transition: background 0.2s;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.dashboard-nav a:hover {
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
|
||||
.alerts-section {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.alert-card {
|
||||
padding: 16px 20px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.alert-card.error {
|
||||
background: #fef2f2;
|
||||
border-left: 4px solid #ef4444;
|
||||
}
|
||||
|
||||
.alert-card.warning {
|
||||
background: #fff7ed;
|
||||
border-left: 4px solid #ff7a00;
|
||||
}
|
||||
|
||||
.alert-card.info {
|
||||
background: #eff6ff;
|
||||
border-left: 4px solid #0693e3;
|
||||
}
|
||||
|
||||
.alert-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.alert-icon {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.alert-message {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #1f2937;
|
||||
}
|
||||
|
||||
.alert-action {
|
||||
padding: 8px 16px;
|
||||
background: white;
|
||||
border: 1px solid #d1d5db;
|
||||
border-radius: 6px;
|
||||
text-decoration: none;
|
||||
color: #374151;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.alert-action:hover {
|
||||
background: #f9fafb;
|
||||
border-color: #9ca3af;
|
||||
}
|
||||
|
||||
.metrics-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 20px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.metric-card {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 24px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||
border-top: 4px solid #0693e3;
|
||||
}
|
||||
|
||||
.metric-card h3 {
|
||||
margin: 0 0 20px 0;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #0d1b2a;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.metric-card .icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.metric-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px 0;
|
||||
border-bottom: 1px solid #f3f4f6;
|
||||
}
|
||||
|
||||
.metric-row:last-child {
|
||||
border-bottom: none;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.metric-label {
|
||||
font-size: 14px;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.metric-value {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #1f2937;
|
||||
}
|
||||
|
||||
.metric-value.success {
|
||||
color: #0bbf87;
|
||||
}
|
||||
|
||||
.metric-value.warning {
|
||||
color: #ff7a00;
|
||||
}
|
||||
|
||||
.metric-value.error {
|
||||
color: #ef4444;
|
||||
}
|
||||
|
||||
.metric-value.info {
|
||||
color: #0693e3;
|
||||
}
|
||||
|
||||
.quick-actions {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 24px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.quick-actions h3 {
|
||||
margin: 0 0 20px 0;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #0d1b2a;
|
||||
}
|
||||
|
||||
.action-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.action-button {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
padding: 20px 16px;
|
||||
background: linear-gradient(135deg, #0693e3 0%, #0d82c8 100%);
|
||||
color: white !important;
|
||||
text-decoration: none;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
box-shadow: 0 2px 4px rgba(6, 147, 227, 0.2);
|
||||
min-height: 80px;
|
||||
}
|
||||
|
||||
.action-button:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 8px rgba(6, 147, 227, 0.3);
|
||||
color: white !important;
|
||||
background: linear-gradient(135deg, #0780cb 0%, #0c75b5 100%);
|
||||
}
|
||||
|
||||
.action-button .icon {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.action-button .label {
|
||||
color: white !important;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.no-alerts {
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
color: #6b7280;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.no-alerts .icon {
|
||||
font-size: 48px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="dashboard-container">
|
||||
<div class="dashboard-header">
|
||||
<div class="dashboard-header-content">
|
||||
<h1>🚀 IGNY8 Admin Dashboard</h1>
|
||||
<p>Real-time operational metrics and system health monitoring</p>
|
||||
</div>
|
||||
<div class="dashboard-nav">
|
||||
<a href="/admin/">← Back to Admin</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if alerts %}
|
||||
<div class="alerts-section">
|
||||
<h2 style="margin: 0 0 16px 0; font-size: 20px; font-weight: 600;">📢 Active Alerts</h2>
|
||||
{% for alert in alerts %}
|
||||
<div class="alert-card {{ alert.level }}">
|
||||
<div class="alert-content">
|
||||
<span class="alert-icon">{{ alert.icon }}</span>
|
||||
<span class="alert-message">{{ alert.message }}</span>
|
||||
</div>
|
||||
<a href="{{ alert.url }}" class="alert-action">{{ alert.action }}</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alerts-section">
|
||||
<div class="no-alerts">
|
||||
<div class="icon">✅</div>
|
||||
<h3 style="margin: 0 0 8px 0; color: #0bbf87;">All Systems Operational</h3>
|
||||
<p style="margin: 0;">No active alerts or issues detected</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="metrics-grid">
|
||||
<!-- Accounts Card -->
|
||||
<div class="metric-card">
|
||||
<h3><span class="icon">👥</span> Accounts</h3>
|
||||
<div class="metric-row">
|
||||
<span class="metric-label">Total Accounts</span>
|
||||
<span class="metric-value info">{{ accounts.total }}</span>
|
||||
</div>
|
||||
<div class="metric-row">
|
||||
<span class="metric-label">Active Accounts</span>
|
||||
<span class="metric-value success">{{ accounts.active }}</span>
|
||||
</div>
|
||||
<div class="metric-row">
|
||||
<span class="metric-label">Low Credit Accounts</span>
|
||||
<span class="metric-value {% if accounts.low_credit > 0 %}warning{% else %}success{% endif %}">{{ accounts.low_credit }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Content Card -->
|
||||
<div class="metric-card">
|
||||
<h3><span class="icon">📚</span> Content</h3>
|
||||
<div class="metric-row">
|
||||
<span class="metric-label">Created This Week</span>
|
||||
<span class="metric-value info">{{ content.this_week }}</span>
|
||||
</div>
|
||||
<div class="metric-row">
|
||||
<span class="metric-label">Created This Month</span>
|
||||
<span class="metric-value info">{{ content.this_month }}</span>
|
||||
</div>
|
||||
<div class="metric-row">
|
||||
<span class="metric-label">Pending Tasks</span>
|
||||
<span class="metric-value {% if content.tasks_pending > 50 %}warning{% else %}info{% endif %}">{{ content.tasks_pending }}</span>
|
||||
</div>
|
||||
<div class="metric-row">
|
||||
<span class="metric-label">In Progress</span>
|
||||
<span class="metric-value info">{{ content.tasks_in_progress }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Billing Card -->
|
||||
<div class="metric-card">
|
||||
<h3><span class="icon">💰</span> Billing</h3>
|
||||
<div class="metric-row">
|
||||
<span class="metric-label">Pending Payments</span>
|
||||
<span class="metric-value {% if billing.pending_payments > 0 %}warning{% else %}success{% endif %}">{{ billing.pending_payments }}</span>
|
||||
</div>
|
||||
<div class="metric-row">
|
||||
<span class="metric-label">Revenue This Month</span>
|
||||
<span class="metric-value success">${{ billing.payments_this_month|floatformat:2 }}</span>
|
||||
</div>
|
||||
<div class="metric-row">
|
||||
<span class="metric-label">Credits Used This Month</span>
|
||||
<span class="metric-value info">{{ billing.credit_usage_this_month }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Automation & Integration Card -->
|
||||
<div class="metric-card">
|
||||
<h3><span class="icon">🤖</span> Automation & Sync</h3>
|
||||
<div class="metric-row">
|
||||
<span class="metric-label">Automations Running</span>
|
||||
<span class="metric-value info">{{ automation.running }}</span>
|
||||
</div>
|
||||
<div class="metric-row">
|
||||
<span class="metric-label">Failed This Week</span>
|
||||
<span class="metric-value {% if automation.failed_this_week > 0 %}error{% else %}success{% endif %}">{{ automation.failed_this_week }}</span>
|
||||
</div>
|
||||
<div class="metric-row">
|
||||
<span class="metric-label">Failed Syncs Today</span>
|
||||
<span class="metric-value {% if integration.sync_failed_today > 5 %}warning{% elif integration.sync_failed_today > 0 %}info{% else %}success{% endif %}">{{ integration.sync_failed_today }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Celery Tasks Card -->
|
||||
<div class="metric-card">
|
||||
<h3><span class="icon">⚙️</span> Celery Tasks</h3>
|
||||
<div class="metric-row">
|
||||
<span class="metric-label">Failed Today</span>
|
||||
<span class="metric-value {% if celery.failed_today > 0 %}error{% else %}success{% endif %}">{{ celery.failed_today }}</span>
|
||||
</div>
|
||||
<div class="metric-row">
|
||||
<span class="metric-label">Pending Tasks</span>
|
||||
<span class="metric-value info">{{ celery.pending }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="quick-actions">
|
||||
<h3>⚡ Quick Actions</h3>
|
||||
<div class="action-grid">
|
||||
<a href="/admin/" class="action-button" style="background: linear-gradient(135deg, #0d1b2a 0%, #1a2e44 100%);">
|
||||
<span class="icon">🏠</span>
|
||||
<span class="label">Admin Home</span>
|
||||
</a>
|
||||
<a href="/admin/igny8_core_auth/account/" class="action-button">
|
||||
<span class="icon">👥</span>
|
||||
<span class="label">Manage Accounts</span>
|
||||
</a>
|
||||
<a href="/admin/writer/content/" class="action-button">
|
||||
<span class="icon">📝</span>
|
||||
<span class="label">View Content</span>
|
||||
</a>
|
||||
<a href="/admin/writer/tasks/" class="action-button">
|
||||
<span class="icon">✅</span>
|
||||
<span class="label">Manage Tasks</span>
|
||||
</a>
|
||||
<a href="/admin/billing/payment/" class="action-button">
|
||||
<span class="icon">💳</span>
|
||||
<span class="label">Review Payments</span>
|
||||
</a>
|
||||
<a href="/admin/automation/automationrun/" class="action-button">
|
||||
<span class="icon">🤖</span>
|
||||
<span class="label">View Automations</span>
|
||||
</a>
|
||||
<a href="/admin/django_celery_results/taskresult/" class="action-button">
|
||||
<span class="icon">⚙️</span>
|
||||
<span class="label">Celery Monitor</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,13 +1,17 @@
|
||||
/* ===================================================================
|
||||
IGNY8 CUSTOM ADMIN STYLES
|
||||
IGNY8 CUSTOM ADMIN STYLES - COMPLETE REDESIGN
|
||||
===================================================================
|
||||
Using IGNY8 brand colors from frontend design system
|
||||
Using exact IGNY8 brand colors from frontend design system
|
||||
=================================================================== */
|
||||
|
||||
/* IGNY8 Brand Color Variables */
|
||||
/* IGNY8 Brand Color Variables - Matching Frontend App */
|
||||
:root {
|
||||
/* Primary Colors */
|
||||
--igny8-primary: #0693e3; /* Primary brand blue */
|
||||
--igny8-primary-dark: #0472b8; /* Primary dark */
|
||||
--igny8-primary-light: #3da9e8; /* Primary light */
|
||||
|
||||
/* Accent Colors */
|
||||
--igny8-success: #0bbf87; /* Success teal-green */
|
||||
--igny8-success-dark: #08966b; /* Success dark */
|
||||
--igny8-warning: #ff7a00; /* Warning orange */
|
||||
@@ -16,34 +20,560 @@
|
||||
--igny8-danger-dark: #d13333; /* Danger dark */
|
||||
--igny8-purple: #5d4ae3; /* Purple accent */
|
||||
--igny8-purple-dark: #3a2f94; /* Purple dark */
|
||||
|
||||
/* Neutral Colors */
|
||||
--igny8-navy: #0d1b2a; /* Dark navy background */
|
||||
--igny8-navy-light: #142b3f; /* Navy light */
|
||||
--igny8-navy-light: #1a2e44; /* Navy light */
|
||||
--igny8-surface: #f8fafc; /* Page background */
|
||||
--igny8-panel: #ffffff; /* Panel background */
|
||||
--igny8-text: #555a68; /* Main text */
|
||||
--igny8-text-dim: #64748b; /* Dimmed text */
|
||||
--igny8-text: #1e293b; /* Main text */
|
||||
--igny8-text-light: #64748b; /* Light text */
|
||||
--igny8-text-dim: #94a3b8; /* Dimmed text */
|
||||
--igny8-stroke: #e2e8f0; /* Borders */
|
||||
--igny8-stroke-dark: #cbd5e1; /* Dark borders */
|
||||
}
|
||||
|
||||
/* ===================================================================
|
||||
HEADER & BRANDING - IGNY8 Primary Blue with Gradient
|
||||
GLOBAL RESETS
|
||||
=================================================================== */
|
||||
body {
|
||||
background: var(--igny8-surface) !important;
|
||||
color: var(--igny8-text) !important;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !important;
|
||||
}
|
||||
|
||||
/* ===================================================================
|
||||
HEADER - Clean Professional Design
|
||||
=================================================================== */
|
||||
#header {
|
||||
background: linear-gradient(135deg, var(--igny8-primary) 0%, var(--igny8-primary-dark) 100%) !important;
|
||||
color: white !important;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1) !important;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.1) !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
#header a:link, #header a:visited {
|
||||
#branding {
|
||||
padding: 16px 30px !important;
|
||||
}
|
||||
|
||||
#branding h1 {
|
||||
margin: 0 !important;
|
||||
font-size: 20px !important;
|
||||
font-weight: 600 !important;
|
||||
}
|
||||
|
||||
#branding h1 a:link,
|
||||
#branding h1 a:visited {
|
||||
color: white !important;
|
||||
text-decoration: none !important;
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
gap: 10px !important;
|
||||
}
|
||||
|
||||
#header a:link,
|
||||
#header a:visited {
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
#branding h1, #branding h1 a:link, #branding h1 a:visited {
|
||||
color: white !important;
|
||||
/* ===================================================================
|
||||
ADD RECORD BUTTON - Modern Accent Color
|
||||
=================================================================== */
|
||||
.object-tools {
|
||||
margin-bottom: 24px !important;
|
||||
float: right !important;
|
||||
}
|
||||
|
||||
.header-user-tools a {
|
||||
.object-tools li {
|
||||
margin: 0 !important;
|
||||
list-style: none !important;
|
||||
}
|
||||
|
||||
.object-tools a.addlink {
|
||||
background: linear-gradient(135deg, var(--igny8-success) 0%, var(--igny8-success-dark) 100%) !important;
|
||||
color: white !important;
|
||||
padding: 12px 28px !important;
|
||||
border-radius: 8px !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 14px !important;
|
||||
text-decoration: none !important;
|
||||
display: inline-flex !important;
|
||||
align-items: center !important;
|
||||
gap: 10px !important;
|
||||
box-shadow: 0 2px 6px rgba(11, 191, 135, 0.3) !important;
|
||||
transition: all 0.2s ease !important;
|
||||
border: none !important;
|
||||
text-transform: none !important;
|
||||
}
|
||||
|
||||
.object-tools a.addlink:before {
|
||||
content: "\f067" !important;
|
||||
font-family: "Font Awesome 6 Free" !important;
|
||||
font-weight: 900 !important;
|
||||
font-size: 14px !important;
|
||||
margin-right: 0 !important;
|
||||
background: none !important;
|
||||
border: none !important;
|
||||
width: auto !important;
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
.object-tools a.addlink:hover {
|
||||
background: linear-gradient(135deg, var(--igny8-success-dark) 0%, #067354 100%) !important;
|
||||
box-shadow: 0 4px 12px rgba(11, 191, 135, 0.4) !important;
|
||||
transform: translateY(-2px) !important;
|
||||
}
|
||||
|
||||
/* ===================================================================
|
||||
EXPORT BUTTON - Purple Accent
|
||||
=================================================================== */
|
||||
a[href*="export"],
|
||||
.export-button,
|
||||
input[name="_export"] {
|
||||
background: linear-gradient(135deg, var(--igny8-purple) 0%, var(--igny8-purple-dark) 100%) !important;
|
||||
color: white !important;
|
||||
padding: 10px 24px !important;
|
||||
border-radius: 8px !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 13px !important;
|
||||
text-decoration: none !important;
|
||||
display: inline-flex !important;
|
||||
align-items: center !important;
|
||||
gap: 8px !important;
|
||||
box-shadow: 0 2px 6px rgba(93, 74, 227, 0.3) !important;
|
||||
transition: all 0.2s ease !important;
|
||||
border: none !important;
|
||||
cursor: pointer !important;
|
||||
}
|
||||
|
||||
a[href*="export"]:hover,
|
||||
.export-button:hover,
|
||||
input[name="_export"]:hover {
|
||||
background: linear-gradient(135deg, var(--igny8-purple-dark) 0%, #2a1f6b 100%) !important;
|
||||
box-shadow: 0 4px 12px rgba(93, 74, 227, 0.4) !important;
|
||||
transform: translateY(-2px) !important;
|
||||
}
|
||||
|
||||
/* ===================================================================
|
||||
SEARCH BAR & TOOLBAR - Professional Layout
|
||||
=================================================================== */
|
||||
#toolbar {
|
||||
padding: 20px 24px !important;
|
||||
background: var(--igny8-panel) !important;
|
||||
border: 1px solid var(--igny8-stroke) !important;
|
||||
border-radius: 12px !important;
|
||||
margin-bottom: 24px !important;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.05) !important;
|
||||
}
|
||||
|
||||
#toolbar form {
|
||||
display: flex !important;
|
||||
gap: 12px !important;
|
||||
align-items: center !important;
|
||||
}
|
||||
|
||||
#toolbar input[type="text"],
|
||||
#searchbar {
|
||||
flex: 1 !important;
|
||||
min-width: 320px !important;
|
||||
max-width: 500px !important;
|
||||
padding: 12px 18px !important;
|
||||
border: 1.5px solid var(--igny8-stroke) !important;
|
||||
border-radius: 8px !important;
|
||||
font-size: 14px !important;
|
||||
transition: all 0.2s ease !important;
|
||||
background: var(--igny8-panel) !important;
|
||||
color: var(--igny8-text) !important;
|
||||
}
|
||||
|
||||
#toolbar input[type="text"]:focus,
|
||||
#searchbar:focus {
|
||||
border-color: var(--igny8-primary) !important;
|
||||
outline: none !important;
|
||||
box-shadow: 0 0 0 4px rgba(6, 147, 227, 0.1) !important;
|
||||
}
|
||||
|
||||
#toolbar input[type="submit"],
|
||||
#toolbar button[type="submit"] {
|
||||
background: linear-gradient(135deg, var(--igny8-primary) 0%, var(--igny8-primary-dark) 100%) !important;
|
||||
color: white !important;
|
||||
padding: 12px 28px !important;
|
||||
border: none !important;
|
||||
border-radius: 8px !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 14px !important;
|
||||
cursor: pointer !important;
|
||||
transition: all 0.2s ease !important;
|
||||
box-shadow: 0 2px 6px rgba(6, 147, 227, 0.3) !important;
|
||||
}
|
||||
|
||||
#toolbar input[type="submit"]:hover,
|
||||
#toolbar button[type="submit"]:hover {
|
||||
background: linear-gradient(135deg, var(--igny8-primary-dark) 0%, #035a8f 100%) !important;
|
||||
box-shadow: 0 4px 12px rgba(6, 147, 227, 0.4) !important;
|
||||
transform: translateY(-2px) !important;
|
||||
}
|
||||
|
||||
/* ===================================================================
|
||||
ACTIONS BAR - Better Styling
|
||||
=================================================================== */
|
||||
#changelist-form .actions {
|
||||
padding: 20px 24px !important;
|
||||
background: var(--igny8-panel) !important;
|
||||
border: 1px solid var(--igny8-stroke) !important;
|
||||
border-radius: 12px !important;
|
||||
margin-bottom: 24px !important;
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
gap: 16px !important;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.05) !important;
|
||||
}
|
||||
|
||||
#changelist-form .actions label {
|
||||
font-weight: 600 !important;
|
||||
color: var(--igny8-text) !important;
|
||||
font-size: 14px !important;
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
#changelist-form .actions select {
|
||||
min-width: 240px !important;
|
||||
padding: 12px 18px !important;
|
||||
border: 1.5px solid var(--igny8-stroke) !important;
|
||||
border-radius: 8px !important;
|
||||
font-size: 14px !important;
|
||||
transition: all 0.2s ease !important;
|
||||
background: var(--igny8-panel) !important;
|
||||
color: var(--igny8-text) !important;
|
||||
}
|
||||
|
||||
#changelist-form .actions select:focus {
|
||||
border-color: var(--igny8-primary) !important;
|
||||
outline: none !important;
|
||||
box-shadow: 0 0 0 4px rgba(6, 147, 227, 0.1) !important;
|
||||
}
|
||||
|
||||
#changelist-form .actions button,
|
||||
#changelist-form .actions input[type="submit"] {
|
||||
background: linear-gradient(135deg, var(--igny8-primary) 0%, var(--igny8-primary-dark) 100%) !important;
|
||||
color: white !important;
|
||||
padding: 12px 28px !important;
|
||||
border: none !important;
|
||||
border-radius: 8px !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 14px !important;
|
||||
cursor: pointer !important;
|
||||
transition: all 0.2s ease !important;
|
||||
box-shadow: 0 2px 6px rgba(6, 147, 227, 0.3) !important;
|
||||
}
|
||||
|
||||
#changelist-form .actions button:hover,
|
||||
#changelist-form .actions input[type="submit"]:hover {
|
||||
background: linear-gradient(135deg, var(--igny8-primary-dark) 0%, #035a8f 100%) !important;
|
||||
box-shadow: 0 4px 12px rgba(6, 147, 227, 0.4) !important;
|
||||
transform: translateY(-2px) !important;
|
||||
}
|
||||
|
||||
/* ===================================================================
|
||||
FILTERS PANEL - Clear Organization
|
||||
=================================================================== */
|
||||
#changelist-filter {
|
||||
background: var(--igny8-panel) !important;
|
||||
border: 1px solid var(--igny8-stroke) !important;
|
||||
border-radius: 12px !important;
|
||||
padding: 0 !important;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.05) !important;
|
||||
}
|
||||
|
||||
#changelist-filter h2 {
|
||||
background: linear-gradient(135deg, var(--igny8-navy) 0%, var(--igny8-navy-light) 100%) !important;
|
||||
color: white !important;
|
||||
padding: 18px 24px !important;
|
||||
margin: 0 !important;
|
||||
font-size: 16px !important;
|
||||
font-weight: 600 !important;
|
||||
border-radius: 12px 12px 0 0 !important;
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
gap: 10px !important;
|
||||
}
|
||||
|
||||
#changelist-filter h2:before {
|
||||
content: "\f0b0" !important;
|
||||
font-family: "Font Awesome 6 Free" !important;
|
||||
font-weight: 900 !important;
|
||||
}
|
||||
|
||||
#changelist-filter h3 {
|
||||
background: var(--igny8-surface) !important;
|
||||
color: var(--igny8-text) !important;
|
||||
padding: 14px 24px !important;
|
||||
margin: 0 !important;
|
||||
font-size: 14px !important;
|
||||
font-weight: 600 !important;
|
||||
border-top: 1px solid var(--igny8-stroke) !important;
|
||||
border-bottom: 1px solid var(--igny8-stroke) !important;
|
||||
}
|
||||
|
||||
#changelist-filter ul {
|
||||
padding: 16px 24px !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
#changelist-filter li {
|
||||
padding: 0 !important;
|
||||
margin: 0 0 8px 0 !important;
|
||||
list-style: none !important;
|
||||
}
|
||||
|
||||
#changelist-filter a {
|
||||
color: var(--igny8-text) !important;
|
||||
text-decoration: none !important;
|
||||
display: block !important;
|
||||
padding: 10px 16px !important;
|
||||
border-radius: 6px !important;
|
||||
transition: all 0.2s ease !important;
|
||||
font-size: 14px !important;
|
||||
}
|
||||
|
||||
#changelist-filter a:hover {
|
||||
background: var(--igny8-surface) !important;
|
||||
color: var(--igny8-primary) !important;
|
||||
}
|
||||
|
||||
#changelist-filter a.selected {
|
||||
background: linear-gradient(135deg, var(--igny8-primary) 0%, var(--igny8-primary-dark) 100%) !important;
|
||||
color: white !important;
|
||||
font-weight: 600 !important;
|
||||
box-shadow: 0 2px 4px rgba(6, 147, 227, 0.2) !important;
|
||||
}
|
||||
|
||||
#changelist-filter .quiet {
|
||||
color: var(--igny8-text-dim) !important;
|
||||
font-size: 13px !important;
|
||||
}
|
||||
|
||||
/* ===================================================================
|
||||
ADD RECORD BUTTON - Clean Professional Style
|
||||
=================================================================== */
|
||||
.object-tools {
|
||||
margin-bottom: 20px !important;
|
||||
}
|
||||
|
||||
.object-tools li {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.object-tools a.addlink {
|
||||
background: linear-gradient(135deg, var(--igny8-primary) 0%, var(--igny8-primary-dark) 100%) !important;
|
||||
color: white !important;
|
||||
padding: 12px 24px !important;
|
||||
border-radius: 8px !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 14px !important;
|
||||
text-decoration: none !important;
|
||||
display: inline-flex !important;
|
||||
align-items: center !important;
|
||||
gap: 8px !important;
|
||||
box-shadow: 0 2px 4px rgba(6, 147, 227, 0.3) !important;
|
||||
transition: all 0.2s ease !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.object-tools a.addlink:before {
|
||||
content: "+" !important;
|
||||
font-size: 18px !important;
|
||||
font-weight: bold !important;
|
||||
margin-right: 0 !important;
|
||||
background: none !important;
|
||||
border: none !important;
|
||||
width: auto !important;
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
.object-tools a.addlink:hover {
|
||||
background: linear-gradient(135deg, var(--igny8-primary-dark) 0%, #035a8f 100%) !important;
|
||||
box-shadow: 0 4px 8px rgba(6, 147, 227, 0.4) !important;
|
||||
transform: translateY(-1px) !important;
|
||||
}
|
||||
|
||||
/* Remove the background from the + icon */
|
||||
.object-tools a.addlink:before {
|
||||
background: none !important;
|
||||
}
|
||||
|
||||
/* ===================================================================
|
||||
SEARCH & ACTION BAR - Better Sizing and Layout
|
||||
=================================================================== */
|
||||
#toolbar {
|
||||
padding: 16px 20px !important;
|
||||
background: var(--igny8-panel) !important;
|
||||
border: 1px solid var(--igny8-stroke) !important;
|
||||
border-radius: 8px !important;
|
||||
margin-bottom: 20px !important;
|
||||
display: flex !important;
|
||||
gap: 16px !important;
|
||||
align-items: center !important;
|
||||
flex-wrap: wrap !important;
|
||||
}
|
||||
|
||||
#toolbar form {
|
||||
display: flex !important;
|
||||
gap: 12px !important;
|
||||
align-items: center !important;
|
||||
flex: 1 !important;
|
||||
max-width: 600px !important;
|
||||
}
|
||||
|
||||
#toolbar input[type="text"] {
|
||||
flex: 1 !important;
|
||||
min-width: 250px !important;
|
||||
padding: 10px 16px !important;
|
||||
border: 1px solid var(--igny8-stroke) !important;
|
||||
border-radius: 6px !important;
|
||||
font-size: 14px !important;
|
||||
transition: all 0.2s ease !important;
|
||||
}
|
||||
|
||||
#toolbar input[type="text"]:focus {
|
||||
border-color: var(--igny8-primary) !important;
|
||||
outline: none !important;
|
||||
box-shadow: 0 0 0 3px rgba(6, 147, 227, 0.1) !important;
|
||||
}
|
||||
|
||||
#toolbar input[type="submit"],
|
||||
#toolbar button[type="submit"] {
|
||||
background: linear-gradient(135deg, var(--igny8-primary) 0%, var(--igny8-primary-dark) 100%) !important;
|
||||
color: white !important;
|
||||
padding: 10px 24px !important;
|
||||
border: none !important;
|
||||
border-radius: 6px !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 14px !important;
|
||||
cursor: pointer !important;
|
||||
transition: all 0.2s ease !important;
|
||||
box-shadow: 0 2px 4px rgba(6, 147, 227, 0.2) !important;
|
||||
}
|
||||
|
||||
#toolbar input[type="submit"]:hover,
|
||||
#toolbar button[type="submit"]:hover {
|
||||
background: linear-gradient(135deg, var(--igny8-primary-dark) 0%, #035a8f 100%) !important;
|
||||
box-shadow: 0 4px 8px rgba(6, 147, 227, 0.3) !important;
|
||||
transform: translateY(-1px) !important;
|
||||
}
|
||||
|
||||
/* Action dropdown */
|
||||
#changelist-form .actions {
|
||||
padding: 16px 20px !important;
|
||||
background: var(--igny8-panel) !important;
|
||||
border: 1px solid var(--igny8-stroke) !important;
|
||||
border-radius: 8px !important;
|
||||
margin-bottom: 20px !important;
|
||||
}
|
||||
|
||||
#changelist-form .actions select {
|
||||
min-width: 220px !important;
|
||||
padding: 10px 16px !important;
|
||||
border: 1px solid var(--igny8-stroke) !important;
|
||||
border-radius: 6px !important;
|
||||
font-size: 14px !important;
|
||||
margin-right: 12px !important;
|
||||
transition: all 0.2s ease !important;
|
||||
}
|
||||
|
||||
#changelist-form .actions select:focus {
|
||||
border-color: var(--igny8-primary) !important;
|
||||
outline: none !important;
|
||||
box-shadow: 0 0 0 3px rgba(6, 147, 227, 0.1) !important;
|
||||
}
|
||||
|
||||
#changelist-form .actions button,
|
||||
#changelist-form .actions input[type="submit"] {
|
||||
background: linear-gradient(135deg, var(--igny8-primary) 0%, var(--igny8-primary-dark) 100%) !important;
|
||||
color: white !important;
|
||||
padding: 10px 24px !important;
|
||||
border: none !important;
|
||||
border-radius: 6px !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 14px !important;
|
||||
cursor: pointer !important;
|
||||
transition: all 0.2s ease !important;
|
||||
box-shadow: 0 2px 4px rgba(6, 147, 227, 0.2) !important;
|
||||
}
|
||||
|
||||
#changelist-form .actions button:hover,
|
||||
#changelist-form .actions input[type="submit"]:hover {
|
||||
background: linear-gradient(135deg, var(--igny8-primary-dark) 0%, #035a8f 100%) !important;
|
||||
box-shadow: 0 4px 8px rgba(6, 147, 227, 0.3) !important;
|
||||
transform: translateY(-1px) !important;
|
||||
}
|
||||
|
||||
/* ===================================================================
|
||||
FILTERS - Clear Labels and Better Organization
|
||||
=================================================================== */
|
||||
#changelist-filter {
|
||||
background: var(--igny8-panel) !important;
|
||||
border: 1px solid var(--igny8-stroke) !important;
|
||||
border-radius: 8px !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
#changelist-filter h2 {
|
||||
background: linear-gradient(135deg, var(--igny8-navy) 0%, var(--igny8-navy-light) 100%) !important;
|
||||
color: white !important;
|
||||
padding: 16px 20px !important;
|
||||
margin: 0 !important;
|
||||
font-size: 16px !important;
|
||||
font-weight: 600 !important;
|
||||
border-radius: 8px 8px 0 0 !important;
|
||||
}
|
||||
|
||||
#changelist-filter h3 {
|
||||
background: var(--igny8-surface) !important;
|
||||
color: var(--igny8-text) !important;
|
||||
padding: 12px 20px !important;
|
||||
margin: 0 !important;
|
||||
font-size: 14px !important;
|
||||
font-weight: 600 !important;
|
||||
border-top: 1px solid var(--igny8-stroke) !important;
|
||||
border-bottom: 1px solid var(--igny8-stroke) !important;
|
||||
}
|
||||
|
||||
#changelist-filter ul {
|
||||
padding: 12px 20px !important;
|
||||
margin: 0 0 16px 0 !important;
|
||||
}
|
||||
|
||||
#changelist-filter li {
|
||||
padding: 8px 0 !important;
|
||||
margin: 0 !important;
|
||||
list-style: none !important;
|
||||
}
|
||||
|
||||
#changelist-filter a {
|
||||
color: var(--igny8-text) !important;
|
||||
text-decoration: none !important;
|
||||
display: block !important;
|
||||
padding: 6px 12px !important;
|
||||
border-radius: 4px !important;
|
||||
transition: all 0.2s ease !important;
|
||||
font-size: 14px !important;
|
||||
}
|
||||
|
||||
#changelist-filter a:hover {
|
||||
background: var(--igny8-surface) !important;
|
||||
color: var(--igny8-primary) !important;
|
||||
}
|
||||
|
||||
#changelist-filter a.selected {
|
||||
background: linear-gradient(135deg, var(--igny8-primary) 0%, var(--igny8-primary-dark) 100%) !important;
|
||||
color: white !important;
|
||||
font-weight: 600 !important;
|
||||
}
|
||||
|
||||
#changelist-filter .quiet {
|
||||
color: var(--igny8-text-dim) !important;
|
||||
font-size: 13px !important;
|
||||
}
|
||||
|
||||
/* ===================================================================
|
||||
@@ -271,6 +801,217 @@
|
||||
background: var(--igny8-danger-dark) !important;
|
||||
}
|
||||
|
||||
/* ===================================================================
|
||||
SIDEBAR MODULE "ADD" LINKS - Icon Only with Theme Colors
|
||||
=================================================================== */
|
||||
.module .addlink,
|
||||
.module a.addlink,
|
||||
#content-main .module .addlink {
|
||||
color: var(--igny8-text) !important;
|
||||
text-decoration: none !important;
|
||||
font-size: 0 !important;
|
||||
padding: 0 !important;
|
||||
display: inline-flex !important;
|
||||
align-items: center !important;
|
||||
justify-content: center !important;
|
||||
transition: all 0.2s ease !important;
|
||||
}
|
||||
|
||||
.module .addlink:before,
|
||||
.module a.addlink:before {
|
||||
content: "\f067" !important;
|
||||
font-family: "Font Awesome 6 Free" !important;
|
||||
font-weight: 900 !important;
|
||||
font-size: 12px !important;
|
||||
display: inline-flex !important;
|
||||
align-items: center !important;
|
||||
justify-content: center !important;
|
||||
width: 24px !important;
|
||||
height: 24px !important;
|
||||
border-radius: 6px !important;
|
||||
background: linear-gradient(135deg, var(--igny8-success) 0%, var(--igny8-success-dark) 100%) !important;
|
||||
color: white !important;
|
||||
margin: 0 !important;
|
||||
transition: all 0.2s ease !important;
|
||||
box-shadow: 0 2px 4px rgba(11, 191, 135, 0.2) !important;
|
||||
}
|
||||
|
||||
.module .addlink:hover:before,
|
||||
.module a.addlink:hover:before {
|
||||
background: linear-gradient(135deg, var(--igny8-success-dark) 0%, #067354 100%) !important;
|
||||
box-shadow: 0 4px 8px rgba(11, 191, 135, 0.3) !important;
|
||||
transform: translateY(-1px) scale(1.05) !important;
|
||||
}
|
||||
|
||||
/* Module navigation styling */
|
||||
.module {
|
||||
margin-bottom: 20px !important;
|
||||
background: var(--igny8-panel) !important;
|
||||
border: 1px solid var(--igny8-stroke) !important;
|
||||
border-radius: 8px !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
.module h2 {
|
||||
background: linear-gradient(135deg, var(--igny8-navy) 0%, var(--igny8-navy-light) 100%) !important;
|
||||
color: white !important;
|
||||
padding: 14px 16px !important;
|
||||
font-size: 14px !important;
|
||||
font-weight: 600 !important;
|
||||
margin: 0 !important;
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
gap: 10px !important;
|
||||
}
|
||||
|
||||
.module table {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.module tr {
|
||||
border-bottom: 1px solid var(--igny8-stroke) !important;
|
||||
}
|
||||
|
||||
.module tr:last-child {
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
||||
.module th,
|
||||
.module td {
|
||||
padding: 12px 16px !important;
|
||||
text-align: left !important;
|
||||
}
|
||||
|
||||
.module th {
|
||||
background: var(--igny8-surface) !important;
|
||||
color: var(--igny8-text) !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 13px !important;
|
||||
}
|
||||
|
||||
.module a {
|
||||
color: var(--igny8-primary) !important;
|
||||
text-decoration: none !important;
|
||||
transition: color 0.2s ease !important;
|
||||
}
|
||||
|
||||
.module a:hover {
|
||||
color: var(--igny8-primary-dark) !important;
|
||||
}
|
||||
|
||||
/* ===================================================================
|
||||
ALL BUTTONS - Consistent Theme Colors
|
||||
=================================================================== */
|
||||
.button,
|
||||
input[type=submit],
|
||||
input[type=button],
|
||||
.submit-row input,
|
||||
button:not(.close) {
|
||||
background: linear-gradient(135deg, var(--igny8-primary) 0%, var(--igny8-primary-dark) 100%) !important;
|
||||
color: white !important;
|
||||
border: none !important;
|
||||
padding: 12px 24px !important;
|
||||
border-radius: 8px !important;
|
||||
cursor: pointer !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 14px !important;
|
||||
transition: all 0.2s ease !important;
|
||||
box-shadow: 0 2px 6px rgba(6, 147, 227, 0.3) !important;
|
||||
}
|
||||
|
||||
.button:hover,
|
||||
input[type=submit]:hover,
|
||||
input[type=button]:hover,
|
||||
button:not(.close):hover {
|
||||
background: linear-gradient(135deg, var(--igny8-primary-dark) 0%, #035a8f 100%) !important;
|
||||
box-shadow: 0 4px 12px rgba(6, 147, 227, 0.4) !important;
|
||||
transform: translateY(-2px) !important;
|
||||
}
|
||||
|
||||
/* Save buttons - Success color */
|
||||
input[name="_save"],
|
||||
input[name="_continue"],
|
||||
.submit-row input[type="submit"]:first-child {
|
||||
background: linear-gradient(135deg, var(--igny8-success) 0%, var(--igny8-success-dark) 100%) !important;
|
||||
box-shadow: 0 2px 6px rgba(11, 191, 135, 0.3) !important;
|
||||
}
|
||||
|
||||
input[name="_save"]:hover,
|
||||
input[name="_continue"]:hover {
|
||||
background: linear-gradient(135deg, var(--igny8-success-dark) 0%, #067354 100%) !important;
|
||||
box-shadow: 0 4px 12px rgba(11, 191, 135, 0.4) !important;
|
||||
}
|
||||
|
||||
/* Delete buttons - Danger color */
|
||||
.deletelink,
|
||||
.deletelink-box a,
|
||||
a.deletelink:link,
|
||||
a.deletelink:visited,
|
||||
input[name="_delete"],
|
||||
.delete-confirmation input[type="submit"] {
|
||||
background: linear-gradient(135deg, var(--igny8-danger) 0%, var(--igny8-danger-dark) 100%) !important;
|
||||
color: white !important;
|
||||
box-shadow: 0 2px 6px rgba(239, 68, 68, 0.3) !important;
|
||||
}
|
||||
|
||||
.deletelink:hover,
|
||||
.deletelink-box a:hover,
|
||||
input[name="_delete"]:hover {
|
||||
background: linear-gradient(135deg, var(--igny8-danger-dark) 0%, #b82222 100%) !important;
|
||||
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.4) !important;
|
||||
}
|
||||
|
||||
/* ===================================================================
|
||||
TABLE IMPROVEMENTS
|
||||
=================================================================== */
|
||||
#result_list {
|
||||
border: 1px solid var(--igny8-stroke) !important;
|
||||
border-radius: 12px !important;
|
||||
overflow: hidden !important;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.05) !important;
|
||||
}
|
||||
|
||||
#result_list thead th {
|
||||
background: linear-gradient(135deg, var(--igny8-surface) 0%, #f1f5f9 100%) !important;
|
||||
color: var(--igny8-text) !important;
|
||||
padding: 16px 12px !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 13px !important;
|
||||
text-transform: uppercase !important;
|
||||
letter-spacing: 0.5px !important;
|
||||
border-bottom: 2px solid var(--igny8-stroke-dark) !important;
|
||||
}
|
||||
|
||||
#result_list thead th a {
|
||||
color: var(--igny8-text) !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
#result_list tbody tr {
|
||||
transition: background-color 0.2s ease !important;
|
||||
}
|
||||
|
||||
#result_list tbody tr:nth-child(odd) {
|
||||
background-color: white !important;
|
||||
}
|
||||
|
||||
#result_list tbody tr:nth-child(even) {
|
||||
background-color: var(--igny8-surface) !important;
|
||||
}
|
||||
|
||||
#result_list tbody tr:hover {
|
||||
background-color: rgba(6, 147, 227, 0.05) !important;
|
||||
}
|
||||
|
||||
#result_list td {
|
||||
padding: 14px 12px !important;
|
||||
color: var(--igny8-text) !important;
|
||||
font-size: 14px !important;
|
||||
border-bottom: 1px solid var(--igny8-stroke) !important;
|
||||
}
|
||||
background: var(--igny8-danger-dark) !important;
|
||||
}
|
||||
|
||||
/* ===================================================================
|
||||
LINKS - IGNY8 Primary Blue
|
||||
=================================================================== */
|
||||
@@ -517,3 +1258,46 @@ fieldset.module h2 {
|
||||
border-left-color: var(--igny8-success);
|
||||
color: var(--igny8-success-dark);
|
||||
}
|
||||
|
||||
/* ===================================================================
|
||||
SIDEBAR MODULE ADD LINKS - Clean and Professional
|
||||
=================================================================== */
|
||||
.module .addlink,
|
||||
.module a.addlink,
|
||||
#content-main .module .addlink {
|
||||
color: var(--igny8-primary) !important;
|
||||
text-decoration: none !important;
|
||||
font-size: 13px !important;
|
||||
font-weight: 500 !important;
|
||||
padding: 6px 10px !important;
|
||||
display: inline-block !important;
|
||||
border-radius: 4px !important;
|
||||
transition: all 0.2s ease !important;
|
||||
}
|
||||
|
||||
.module .addlink:before,
|
||||
.module a.addlink:before {
|
||||
content: "+" !important;
|
||||
display: inline-block !important;
|
||||
width: 18px !important;
|
||||
height: 18px !important;
|
||||
line-height: 18px !important;
|
||||
text-align: center !important;
|
||||
border-radius: 3px !important;
|
||||
background: var(--igny8-primary) !important;
|
||||
color: white !important;
|
||||
margin-right: 6px !important;
|
||||
font-size: 14px !important;
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.module .addlink:hover,
|
||||
.module a.addlink:hover {
|
||||
background: rgba(6, 147, 227, 0.1) !important;
|
||||
color: var(--igny8-primary-dark) !important;
|
||||
}
|
||||
|
||||
.module .addlink:hover:before,
|
||||
.module a.addlink:hover:before {
|
||||
background: var(--igny8-primary-dark) !important;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user