lamost fully fixed umfold template

This commit is contained in:
IGNY8 VPS (Salman)
2025-12-14 17:30:10 +00:00
parent cd2c84116b
commit a6fab8784d
18 changed files with 60 additions and 2125 deletions

View File

@@ -2,11 +2,12 @@
Admin configuration for AI models Admin configuration for AI models
""" """
from django.contrib import admin from django.contrib import admin
from unfold.admin import ModelAdmin
from igny8_core.ai.models import AITaskLog from igny8_core.ai.models import AITaskLog
@admin.register(AITaskLog) @admin.register(AITaskLog)
class AITaskLogAdmin(admin.ModelAdmin): class AITaskLogAdmin(ModelAdmin):
"""Admin interface for AI task logs""" """Admin interface for AI task logs"""
list_display = [ list_display = [
'function_name', 'function_name',

View File

@@ -4,6 +4,7 @@ Admin interface for auth models
from django import forms from django import forms
from django.contrib import admin from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from unfold.admin import ModelAdmin
from igny8_core.admin.base import AccountAdminMixin from igny8_core.admin.base import AccountAdminMixin
from .models import User, Account, Plan, Subscription, Site, Sector, SiteUserAccess, Industry, IndustrySector, SeedKeyword, PasswordResetToken from .models import User, Account, Plan, Subscription, Site, Sector, SiteUserAccess, Industry, IndustrySector, SeedKeyword, PasswordResetToken
@@ -109,7 +110,7 @@ class AccountAdminForm(forms.ModelForm):
@admin.register(Plan) @admin.register(Plan)
class PlanAdmin(admin.ModelAdmin): class PlanAdmin(ModelAdmin):
"""Plan admin - Global, no account filtering needed""" """Plan admin - Global, no account filtering needed"""
list_display = ['name', 'slug', 'price', 'billing_cycle', 'max_sites', 'max_users', 'max_keywords', 'max_content_words', 'included_credits', 'is_active', 'is_featured'] list_display = ['name', 'slug', 'price', 'billing_cycle', 'max_sites', 'max_users', 'max_keywords', 'max_content_words', 'included_credits', 'is_active', 'is_featured']
list_filter = ['is_active', 'billing_cycle', 'is_internal', 'is_featured'] list_filter = ['is_active', 'billing_cycle', 'is_internal', 'is_featured']
@@ -143,7 +144,7 @@ class PlanAdmin(admin.ModelAdmin):
@admin.register(Account) @admin.register(Account)
class AccountAdmin(AccountAdminMixin, admin.ModelAdmin): class AccountAdmin(AccountAdminMixin, ModelAdmin):
form = AccountAdminForm form = AccountAdminForm
list_display = ['name', 'slug', 'owner', 'plan', 'status', 'health_indicator', 'credits', 'created_at'] list_display = ['name', 'slug', 'owner', 'plan', 'status', 'health_indicator', 'credits', 'created_at']
list_filter = ['status', 'plan'] list_filter = ['status', 'plan']
@@ -306,7 +307,7 @@ class AccountAdmin(AccountAdminMixin, admin.ModelAdmin):
@admin.register(Subscription) @admin.register(Subscription)
class SubscriptionAdmin(AccountAdminMixin, admin.ModelAdmin): class SubscriptionAdmin(AccountAdminMixin, ModelAdmin):
list_display = ['account', 'status', 'current_period_start', 'current_period_end'] list_display = ['account', 'status', 'current_period_start', 'current_period_end']
list_filter = ['status'] list_filter = ['status']
search_fields = ['account__name', 'stripe_subscription_id'] search_fields = ['account__name', 'stripe_subscription_id']
@@ -314,7 +315,7 @@ class SubscriptionAdmin(AccountAdminMixin, admin.ModelAdmin):
@admin.register(PasswordResetToken) @admin.register(PasswordResetToken)
class PasswordResetTokenAdmin(admin.ModelAdmin): class PasswordResetTokenAdmin(ModelAdmin):
list_display = ['user', 'token', 'used', 'expires_at', 'created_at'] list_display = ['user', 'token', 'used', 'expires_at', 'created_at']
list_filter = ['used', 'expires_at', 'created_at'] list_filter = ['used', 'expires_at', 'created_at']
search_fields = ['user__email', 'token'] search_fields = ['user__email', 'token']
@@ -352,7 +353,7 @@ class SectorInline(admin.TabularInline):
@admin.register(Site) @admin.register(Site)
class SiteAdmin(AccountAdminMixin, admin.ModelAdmin): class SiteAdmin(AccountAdminMixin, ModelAdmin):
list_display = ['name', 'slug', 'account', 'industry', 'domain', 'status', 'is_active', 'get_api_key_status', 'get_sectors_count'] list_display = ['name', 'slug', 'account', 'industry', 'domain', 'status', 'is_active', 'get_api_key_status', 'get_sectors_count']
list_filter = ['status', 'is_active', 'account', 'industry', 'hosting_type'] list_filter = ['status', 'is_active', 'account', 'industry', 'hosting_type']
search_fields = ['name', 'slug', 'domain', 'industry__name'] search_fields = ['name', 'slug', 'domain', 'industry__name']
@@ -431,7 +432,7 @@ class SiteAdmin(AccountAdminMixin, admin.ModelAdmin):
@admin.register(Sector) @admin.register(Sector)
class SectorAdmin(AccountAdminMixin, admin.ModelAdmin): class SectorAdmin(AccountAdminMixin, ModelAdmin):
list_display = ['name', 'slug', 'site', 'industry_sector', 'get_industry', 'status', 'is_active', 'get_keywords_count', 'get_clusters_count'] list_display = ['name', 'slug', 'site', 'industry_sector', 'get_industry', 'status', 'is_active', 'get_keywords_count', 'get_clusters_count']
list_filter = ['status', 'is_active', 'site', 'industry_sector__industry'] list_filter = ['status', 'is_active', 'site', 'industry_sector__industry']
search_fields = ['name', 'slug', 'site__name', 'industry_sector__name'] search_fields = ['name', 'slug', 'site__name', 'industry_sector__name']
@@ -469,7 +470,7 @@ class SectorAdmin(AccountAdminMixin, admin.ModelAdmin):
@admin.register(SiteUserAccess) @admin.register(SiteUserAccess)
class SiteUserAccessAdmin(admin.ModelAdmin): class SiteUserAccessAdmin(ModelAdmin):
list_display = ['user', 'site', 'granted_at', 'granted_by'] list_display = ['user', 'site', 'granted_at', 'granted_by']
list_filter = ['granted_at'] list_filter = ['granted_at']
search_fields = ['user__email', 'site__name'] search_fields = ['user__email', 'site__name']
@@ -485,14 +486,13 @@ class IndustrySectorInline(admin.TabularInline):
@admin.register(Industry) @admin.register(Industry)
class IndustryAdmin(admin.ModelAdmin): class IndustryAdmin(ModelAdmin):
list_display = ['name', 'slug', 'is_active', 'get_sectors_count', 'created_at'] list_display = ['name', 'slug', 'is_active', 'get_sectors_count', 'created_at']
list_filter = ['is_active'] list_filter = ['is_active']
search_fields = ['name', 'slug', 'description'] search_fields = ['name', 'slug', 'description']
readonly_fields = ['created_at', 'updated_at'] readonly_fields = ['created_at', 'updated_at']
inlines = [IndustrySectorInline] inlines = [IndustrySectorInline]
actions = ['delete_selected'] # Enable bulk delete actions = ['delete_selected'] # Enable bulk delete
change_list_template = 'admin/igny8_core_auth/industry/change_list.html'
def get_sectors_count(self, obj): def get_sectors_count(self, obj):
return obj.sectors.filter(is_active=True).count() return obj.sectors.filter(is_active=True).count()
@@ -504,13 +504,12 @@ class IndustryAdmin(admin.ModelAdmin):
@admin.register(IndustrySector) @admin.register(IndustrySector)
class IndustrySectorAdmin(admin.ModelAdmin): class IndustrySectorAdmin(ModelAdmin):
list_display = ['name', 'slug', 'industry', 'is_active'] list_display = ['name', 'slug', 'industry', 'is_active']
list_filter = ['is_active', 'industry'] list_filter = ['is_active', 'industry']
search_fields = ['name', 'slug', 'description'] search_fields = ['name', 'slug', 'description']
readonly_fields = ['created_at', 'updated_at'] readonly_fields = ['created_at', 'updated_at']
actions = ['delete_selected'] # Enable bulk delete actions = ['delete_selected'] # Enable bulk delete
change_list_template = 'admin/igny8_core_auth/industrysector/change_list.html'
def has_delete_permission(self, request, obj=None): def has_delete_permission(self, request, obj=None):
"""Allow deletion for superusers and developers""" """Allow deletion for superusers and developers"""
@@ -518,14 +517,13 @@ class IndustrySectorAdmin(admin.ModelAdmin):
@admin.register(SeedKeyword) @admin.register(SeedKeyword)
class SeedKeywordAdmin(admin.ModelAdmin): class SeedKeywordAdmin(ModelAdmin):
"""SeedKeyword admin - Global reference data, no account filtering""" """SeedKeyword admin - Global reference data, no account filtering"""
list_display = ['keyword', 'industry', 'sector', 'volume', 'difficulty', 'intent', 'is_active', 'created_at'] list_display = ['keyword', 'industry', 'sector', 'volume', 'difficulty', 'intent', 'is_active', 'created_at']
list_filter = ['is_active', 'industry', 'sector', 'intent'] list_filter = ['is_active', 'industry', 'sector', 'intent']
search_fields = ['keyword'] search_fields = ['keyword']
readonly_fields = ['created_at', 'updated_at'] readonly_fields = ['created_at', 'updated_at']
actions = ['delete_selected'] # Enable bulk delete actions = ['delete_selected'] # Enable bulk delete
change_list_template = 'admin/igny8_core_auth/seedkeyword/change_list.html'
fieldsets = ( fieldsets = (
('Keyword Info', { ('Keyword Info', {

View File

@@ -2,19 +2,20 @@
Admin registration for Automation models Admin registration for Automation models
""" """
from django.contrib import admin from django.contrib import admin
from unfold.admin import ModelAdmin
from igny8_core.admin.base import AccountAdminMixin from igny8_core.admin.base import AccountAdminMixin
from .models import AutomationConfig, AutomationRun from .models import AutomationConfig, AutomationRun
@admin.register(AutomationConfig) @admin.register(AutomationConfig)
class AutomationConfigAdmin(AccountAdminMixin, admin.ModelAdmin): class AutomationConfigAdmin(AccountAdminMixin, ModelAdmin):
list_display = ('site', 'is_enabled', 'frequency', 'scheduled_time', 'within_stage_delay', 'between_stage_delay', 'last_run_at') list_display = ('site', 'is_enabled', 'frequency', 'scheduled_time', 'within_stage_delay', 'between_stage_delay', 'last_run_at')
list_filter = ('is_enabled', 'frequency') list_filter = ('is_enabled', 'frequency')
search_fields = ('site__domain',) search_fields = ('site__domain',)
@admin.register(AutomationRun) @admin.register(AutomationRun)
class AutomationRunAdmin(AccountAdminMixin, admin.ModelAdmin): class AutomationRunAdmin(AccountAdminMixin, ModelAdmin):
list_display = ('run_id', 'site', 'status', 'current_stage', 'started_at', 'completed_at') list_display = ('run_id', 'site', 'status', 'current_stage', 'started_at', 'completed_at')
list_filter = ('status', 'current_stage') list_filter = ('status', 'current_stage')
search_fields = ('run_id', 'site__domain') search_fields = ('run_id', 'site__domain')

View File

@@ -1,10 +1,11 @@
from django.contrib import admin from django.contrib import admin
from unfold.admin import ModelAdmin
from igny8_core.admin.base import AccountAdminMixin from igny8_core.admin.base import AccountAdminMixin
from .models import SiteIntegration, SyncEvent from .models import SiteIntegration, SyncEvent
@admin.register(SiteIntegration) @admin.register(SiteIntegration)
class SiteIntegrationAdmin(AccountAdminMixin, admin.ModelAdmin): class SiteIntegrationAdmin(AccountAdminMixin, ModelAdmin):
list_display = [ list_display = [
'site', 'site',
'platform', 'platform',
@@ -20,7 +21,7 @@ class SiteIntegrationAdmin(AccountAdminMixin, admin.ModelAdmin):
@admin.register(SyncEvent) @admin.register(SyncEvent)
class SyncEventAdmin(AccountAdminMixin, admin.ModelAdmin): class SyncEventAdmin(AccountAdminMixin, ModelAdmin):
list_display = [ list_display = [
'integration', 'integration',
'site', 'site',

View File

@@ -1,10 +1,11 @@
from django.contrib import admin from django.contrib import admin
from unfold.admin import ModelAdmin
from igny8_core.admin.base import AccountAdminMixin from igny8_core.admin.base import AccountAdminMixin
from .models import OptimizationTask from .models import OptimizationTask
@admin.register(OptimizationTask) @admin.register(OptimizationTask)
class OptimizationTaskAdmin(AccountAdminMixin, admin.ModelAdmin): class OptimizationTaskAdmin(AccountAdminMixin, ModelAdmin):
list_display = ['content', 'account', 'status', 'credits_used', 'created_at'] list_display = ['content', 'account', 'status', 'credits_used', 'created_at']
list_filter = ['status', 'created_at'] list_filter = ['status', 'created_at']
search_fields = ['content__title', 'account__name'] search_fields = ['content__title', 'account__name']

View File

@@ -1,10 +1,11 @@
from django.contrib import admin from django.contrib import admin
from unfold.admin import ModelAdmin
from igny8_core.admin.base import SiteSectorAdminMixin from igny8_core.admin.base import SiteSectorAdminMixin
from .models import PublishingRecord, DeploymentRecord from .models import PublishingRecord, DeploymentRecord
@admin.register(PublishingRecord) @admin.register(PublishingRecord)
class PublishingRecordAdmin(SiteSectorAdminMixin, admin.ModelAdmin): class PublishingRecordAdmin(SiteSectorAdminMixin, ModelAdmin):
list_display = [ list_display = [
'content', 'content',
'site', 'site',
@@ -20,7 +21,7 @@ class PublishingRecordAdmin(SiteSectorAdminMixin, admin.ModelAdmin):
@admin.register(DeploymentRecord) @admin.register(DeploymentRecord)
class DeploymentRecordAdmin(SiteSectorAdminMixin, admin.ModelAdmin): class DeploymentRecordAdmin(SiteSectorAdminMixin, ModelAdmin):
list_display = [ list_display = [
'site', 'site',
'sector', 'sector',

View File

@@ -4,6 +4,7 @@ Billing Module Admin
from django.contrib import admin from django.contrib import admin
from django.utils.html import format_html from django.utils.html import format_html
from django.contrib import messages from django.contrib import messages
from unfold.admin import ModelAdmin
from igny8_core.admin.base import AccountAdminMixin from igny8_core.admin.base import AccountAdminMixin
from igny8_core.business.billing.models import ( from igny8_core.business.billing.models import (
CreditCostConfig, CreditCostConfig,
@@ -28,7 +29,7 @@ class CreditTransactionResource(resources.ModelResource):
@admin.register(CreditTransaction) @admin.register(CreditTransaction)
class CreditTransactionAdmin(ExportMixin, AccountAdminMixin, admin.ModelAdmin): class CreditTransactionAdmin(ExportMixin, AccountAdminMixin, ModelAdmin):
resource_class = CreditTransactionResource resource_class = CreditTransactionResource
list_display = ['id', 'account', 'transaction_type', 'amount', 'balance_after', 'description', 'created_at'] list_display = ['id', 'account', 'transaction_type', 'amount', 'balance_after', 'description', 'created_at']
list_filter = ['transaction_type', ('created_at', DateRangeFilter), 'account'] list_filter = ['transaction_type', ('created_at', DateRangeFilter), 'account']
@@ -47,7 +48,7 @@ class CreditTransactionAdmin(ExportMixin, AccountAdminMixin, admin.ModelAdmin):
@admin.register(CreditUsageLog) @admin.register(CreditUsageLog)
class CreditUsageLogAdmin(AccountAdminMixin, admin.ModelAdmin): class CreditUsageLogAdmin(AccountAdminMixin, ModelAdmin):
list_display = ['id', 'account', 'operation_type', 'credits_used', 'cost_usd', 'model_used', 'created_at'] list_display = ['id', 'account', 'operation_type', 'credits_used', 'cost_usd', 'model_used', 'created_at']
list_filter = ['operation_type', 'created_at', 'account', 'model_used'] list_filter = ['operation_type', 'created_at', 'account', 'model_used']
search_fields = ['account__name', 'model_used'] search_fields = ['account__name', 'model_used']
@@ -65,7 +66,7 @@ class CreditUsageLogAdmin(AccountAdminMixin, admin.ModelAdmin):
@admin.register(Invoice) @admin.register(Invoice)
class InvoiceAdmin(AccountAdminMixin, admin.ModelAdmin): class InvoiceAdmin(AccountAdminMixin, ModelAdmin):
list_display = [ list_display = [
'invoice_number', 'invoice_number',
'account', 'account',
@@ -91,7 +92,7 @@ class PaymentResource(resources.ModelResource):
@admin.register(Payment) @admin.register(Payment)
class PaymentAdmin(ExportMixin, AccountAdminMixin, admin.ModelAdmin): class PaymentAdmin(ExportMixin, AccountAdminMixin, ModelAdmin):
""" """
Main Payment Admin with approval workflow. Main Payment Admin with approval workflow.
When you change status to 'succeeded', it automatically: When you change status to 'succeeded', it automatically:
@@ -373,7 +374,7 @@ class PaymentAdmin(ExportMixin, AccountAdminMixin, admin.ModelAdmin):
@admin.register(CreditPackage) @admin.register(CreditPackage)
class CreditPackageAdmin(admin.ModelAdmin): class CreditPackageAdmin(ModelAdmin):
list_display = ['name', 'slug', 'credits', 'price', 'discount_percentage', 'is_active', 'is_featured', 'sort_order'] list_display = ['name', 'slug', 'credits', 'price', 'discount_percentage', 'is_active', 'is_featured', 'sort_order']
list_filter = ['is_active', 'is_featured'] list_filter = ['is_active', 'is_featured']
search_fields = ['name', 'slug'] search_fields = ['name', 'slug']
@@ -381,7 +382,7 @@ class CreditPackageAdmin(admin.ModelAdmin):
@admin.register(PaymentMethodConfig) @admin.register(PaymentMethodConfig)
class PaymentMethodConfigAdmin(admin.ModelAdmin): class PaymentMethodConfigAdmin(ModelAdmin):
list_display = ['country_code', 'payment_method', 'display_name', 'is_enabled', 'sort_order', 'updated_at'] list_display = ['country_code', 'payment_method', 'display_name', 'is_enabled', 'sort_order', 'updated_at']
list_filter = ['payment_method', 'is_enabled', 'country_code'] list_filter = ['payment_method', 'is_enabled', 'country_code']
search_fields = ['country_code', 'display_name', 'payment_method'] search_fields = ['country_code', 'display_name', 'payment_method']
@@ -390,7 +391,7 @@ class PaymentMethodConfigAdmin(admin.ModelAdmin):
@admin.register(AccountPaymentMethod) @admin.register(AccountPaymentMethod)
class AccountPaymentMethodAdmin(AccountAdminMixin, admin.ModelAdmin): class AccountPaymentMethodAdmin(AccountAdminMixin, ModelAdmin):
list_display = [ list_display = [
'display_name', 'display_name',
'type', 'type',
@@ -419,7 +420,7 @@ class AccountPaymentMethodAdmin(AccountAdminMixin, admin.ModelAdmin):
@admin.register(CreditCostConfig) @admin.register(CreditCostConfig)
class CreditCostConfigAdmin(admin.ModelAdmin): class CreditCostConfigAdmin(ModelAdmin):
list_display = [ list_display = [
'operation_type', 'operation_type',
'display_name', 'display_name',

View File

@@ -1,5 +1,6 @@
from django.contrib import admin from django.contrib import admin
from django.contrib import messages from django.contrib import messages
from unfold.admin import ModelAdmin
from igny8_core.admin.base import SiteSectorAdminMixin from igny8_core.admin.base import SiteSectorAdminMixin
from .models import Keywords, Clusters, ContentIdeas from .models import Keywords, Clusters, ContentIdeas
from import_export.admin import ExportMixin from import_export.admin import ExportMixin
@@ -16,7 +17,7 @@ class KeywordsResource(resources.ModelResource):
@admin.register(Clusters) @admin.register(Clusters)
class ClustersAdmin(SiteSectorAdminMixin, admin.ModelAdmin): class ClustersAdmin(SiteSectorAdminMixin, ModelAdmin):
list_display = ['name', 'site', 'sector', 'keywords_count', 'volume', 'status', 'created_at'] list_display = ['name', 'site', 'sector', 'keywords_count', 'volume', 'status', 'created_at']
list_filter = ['status', 'site', 'sector'] list_filter = ['status', 'site', 'sector']
search_fields = ['name'] search_fields = ['name']
@@ -40,7 +41,7 @@ class ClustersAdmin(SiteSectorAdminMixin, admin.ModelAdmin):
@admin.register(Keywords) @admin.register(Keywords)
class KeywordsAdmin(ExportMixin, SiteSectorAdminMixin, admin.ModelAdmin): class KeywordsAdmin(ExportMixin, SiteSectorAdminMixin, ModelAdmin):
resource_class = KeywordsResource resource_class = KeywordsResource
list_display = ['keyword', 'seed_keyword', 'site', 'sector', 'cluster', 'volume', 'difficulty', 'intent', 'status', 'created_at'] 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'] list_filter = ['status', 'seed_keyword__intent', 'site', 'sector', 'seed_keyword__industry', 'seed_keyword__sector']
@@ -130,7 +131,7 @@ class KeywordsAdmin(ExportMixin, SiteSectorAdminMixin, admin.ModelAdmin):
@admin.register(ContentIdeas) @admin.register(ContentIdeas)
class ContentIdeasAdmin(SiteSectorAdminMixin, admin.ModelAdmin): class ContentIdeasAdmin(SiteSectorAdminMixin, ModelAdmin):
list_display = ['idea_title', 'site', 'sector', 'description_preview', 'content_type', 'content_structure', 'status', 'keyword_cluster', 'estimated_word_count', 'created_at'] list_display = ['idea_title', 'site', 'sector', 'description_preview', 'content_type', 'content_structure', 'status', 'keyword_cluster', 'estimated_word_count', 'created_at']
list_filter = ['status', 'content_type', 'content_structure', 'site', 'sector'] list_filter = ['status', 'content_type', 'content_structure', 'site', 'sector']
search_fields = ['idea_title', 'target_keywords', 'description'] search_fields = ['idea_title', 'target_keywords', 'description']

View File

@@ -2,6 +2,7 @@
System Module Admin System Module Admin
""" """
from django.contrib import admin from django.contrib import admin
from unfold.admin import ModelAdmin
from igny8_core.admin.base import AccountAdminMixin from igny8_core.admin.base import AccountAdminMixin
from .models import AIPrompt, IntegrationSettings, AuthorProfile, Strategy from .models import AIPrompt, IntegrationSettings, AuthorProfile, Strategy
@@ -15,7 +16,7 @@ try:
from .models import SystemLog, SystemStatus from .models import SystemLog, SystemStatus
@admin.register(SystemLog) @admin.register(SystemLog)
class SystemLogAdmin(AccountAdminMixin, admin.ModelAdmin): class SystemLogAdmin(AccountAdminMixin, ModelAdmin):
list_display = ['id', 'account', 'module', 'level', 'action', 'message', 'created_at'] list_display = ['id', 'account', 'module', 'level', 'action', 'message', 'created_at']
list_filter = ['module', 'level', 'created_at', 'account'] list_filter = ['module', 'level', 'created_at', 'account']
search_fields = ['message', 'action'] search_fields = ['message', 'action']
@@ -24,7 +25,7 @@ try:
@admin.register(SystemStatus) @admin.register(SystemStatus)
class SystemStatusAdmin(AccountAdminMixin, admin.ModelAdmin): class SystemStatusAdmin(AccountAdminMixin, ModelAdmin):
list_display = ['component', 'account', 'status', 'message', 'last_check'] list_display = ['component', 'account', 'status', 'message', 'last_check']
list_filter = ['status', 'component', 'account'] list_filter = ['status', 'component', 'account']
search_fields = ['component', 'message'] search_fields = ['component', 'message']
@@ -34,7 +35,7 @@ except ImportError:
@admin.register(AIPrompt) @admin.register(AIPrompt)
class AIPromptAdmin(AccountAdminMixin, admin.ModelAdmin): class AIPromptAdmin(AccountAdminMixin, ModelAdmin):
list_display = ['id', 'prompt_type', 'account', 'is_active', 'updated_at'] list_display = ['id', 'prompt_type', 'account', 'is_active', 'updated_at']
list_filter = ['prompt_type', 'is_active', 'account'] list_filter = ['prompt_type', 'is_active', 'account']
search_fields = ['prompt_type'] search_fields = ['prompt_type']
@@ -63,7 +64,7 @@ class AIPromptAdmin(AccountAdminMixin, admin.ModelAdmin):
@admin.register(IntegrationSettings) @admin.register(IntegrationSettings)
class IntegrationSettingsAdmin(AccountAdminMixin, admin.ModelAdmin): class IntegrationSettingsAdmin(AccountAdminMixin, ModelAdmin):
list_display = ['id', 'integration_type', 'account', 'is_active', 'updated_at'] list_display = ['id', 'integration_type', 'account', 'is_active', 'updated_at']
list_filter = ['integration_type', 'is_active', 'account'] list_filter = ['integration_type', 'is_active', 'account']
search_fields = ['integration_type'] search_fields = ['integration_type']
@@ -99,7 +100,7 @@ class IntegrationSettingsAdmin(AccountAdminMixin, admin.ModelAdmin):
@admin.register(AuthorProfile) @admin.register(AuthorProfile)
class AuthorProfileAdmin(AccountAdminMixin, admin.ModelAdmin): class AuthorProfileAdmin(AccountAdminMixin, ModelAdmin):
list_display = ['name', 'account', 'tone', 'language', 'is_active', 'created_at'] list_display = ['name', 'account', 'tone', 'language', 'is_active', 'created_at']
list_filter = ['is_active', 'tone', 'language', 'account'] list_filter = ['is_active', 'tone', 'language', 'account']
search_fields = ['name', 'description', 'tone'] search_fields = ['name', 'description', 'tone']
@@ -128,7 +129,7 @@ class AuthorProfileAdmin(AccountAdminMixin, admin.ModelAdmin):
@admin.register(Strategy) @admin.register(Strategy)
class StrategyAdmin(AccountAdminMixin, admin.ModelAdmin): class StrategyAdmin(AccountAdminMixin, ModelAdmin):
list_display = ['name', 'account', 'sector', 'is_active', 'created_at'] list_display = ['name', 'account', 'sector', 'is_active', 'created_at']
list_filter = ['is_active', 'account'] list_filter = ['is_active', 'account']
search_fields = ['name', 'description'] search_fields = ['name', 'description']

View File

@@ -2,12 +2,13 @@
Settings Models Admin Settings Models Admin
""" """
from django.contrib import admin from django.contrib import admin
from unfold.admin import ModelAdmin
from igny8_core.admin.base import AccountAdminMixin from igny8_core.admin.base import AccountAdminMixin
from .settings_models import SystemSettings, AccountSettings, UserSettings, ModuleSettings, ModuleEnableSettings, AISettings from .settings_models import SystemSettings, AccountSettings, UserSettings, ModuleSettings, ModuleEnableSettings, AISettings
@admin.register(SystemSettings) @admin.register(SystemSettings)
class SystemSettingsAdmin(admin.ModelAdmin): class SystemSettingsAdmin(ModelAdmin):
"""SystemSettings - Global, no account filtering""" """SystemSettings - Global, no account filtering"""
list_display = ['key', 'description', 'updated_at'] list_display = ['key', 'description', 'updated_at']
search_fields = ['key', 'description'] search_fields = ['key', 'description']
@@ -15,7 +16,7 @@ class SystemSettingsAdmin(admin.ModelAdmin):
@admin.register(AccountSettings) @admin.register(AccountSettings)
class AccountSettingsAdmin(AccountAdminMixin, admin.ModelAdmin): class AccountSettingsAdmin(AccountAdminMixin, ModelAdmin):
list_display = ['account', 'key', 'is_active', 'updated_at'] list_display = ['account', 'key', 'is_active', 'updated_at']
list_filter = ['is_active', 'account'] list_filter = ['is_active', 'account']
search_fields = ['key', 'account__name'] search_fields = ['key', 'account__name']
@@ -32,7 +33,7 @@ class AccountSettingsAdmin(AccountAdminMixin, admin.ModelAdmin):
@admin.register(UserSettings) @admin.register(UserSettings)
class UserSettingsAdmin(admin.ModelAdmin): class UserSettingsAdmin(ModelAdmin):
list_display = ['user', 'account', 'key', 'updated_at'] list_display = ['user', 'account', 'key', 'updated_at']
list_filter = ['account'] list_filter = ['account']
search_fields = ['key', 'user__email', 'account__name'] search_fields = ['key', 'user__email', 'account__name']
@@ -59,7 +60,7 @@ class UserSettingsAdmin(admin.ModelAdmin):
@admin.register(ModuleSettings) @admin.register(ModuleSettings)
class ModuleSettingsAdmin(AccountAdminMixin, admin.ModelAdmin): class ModuleSettingsAdmin(AccountAdminMixin, ModelAdmin):
list_display = ['account', 'module_name', 'key', 'is_active', 'updated_at'] list_display = ['account', 'module_name', 'key', 'is_active', 'updated_at']
list_filter = ['module_name', 'is_active', 'account'] list_filter = ['module_name', 'is_active', 'account']
search_fields = ['key', 'module_name', 'account__name'] search_fields = ['key', 'module_name', 'account__name']
@@ -76,7 +77,7 @@ class ModuleSettingsAdmin(AccountAdminMixin, admin.ModelAdmin):
@admin.register(AISettings) @admin.register(AISettings)
class AISettingsAdmin(AccountAdminMixin, admin.ModelAdmin): class AISettingsAdmin(AccountAdminMixin, ModelAdmin):
list_display = ['account', 'integration_type', 'is_active', 'updated_at'] list_display = ['account', 'integration_type', 'is_active', 'updated_at']
list_filter = ['integration_type', 'is_active', 'account'] list_filter = ['integration_type', 'is_active', 'account']
search_fields = ['integration_type', 'account__name'] search_fields = ['integration_type', 'account__name']
@@ -93,7 +94,7 @@ class AISettingsAdmin(AccountAdminMixin, admin.ModelAdmin):
@admin.register(ModuleEnableSettings) @admin.register(ModuleEnableSettings)
class ModuleEnableSettingsAdmin(AccountAdminMixin, admin.ModelAdmin): class ModuleEnableSettingsAdmin(AccountAdminMixin, ModelAdmin):
list_display = [ list_display = [
'account', 'account',
'planner_enabled', 'planner_enabled',

View File

@@ -1,5 +1,6 @@
from django.contrib import admin from django.contrib import admin
from django.contrib import messages from django.contrib import messages
from unfold.admin import ModelAdmin
from igny8_core.admin.base import SiteSectorAdminMixin from igny8_core.admin.base import SiteSectorAdminMixin
from .models import Tasks, Images, Content from .models import Tasks, Images, Content
from igny8_core.business.content.models import ContentTaxonomy, ContentAttribute, ContentTaxonomyRelation, ContentClusterMap from igny8_core.business.content.models import ContentTaxonomy, ContentAttribute, ContentTaxonomyRelation, ContentClusterMap
@@ -26,7 +27,7 @@ class TaskResource(resources.ModelResource):
@admin.register(Tasks) @admin.register(Tasks)
class TasksAdmin(ExportMixin, SiteSectorAdminMixin, admin.ModelAdmin): class TasksAdmin(ExportMixin, SiteSectorAdminMixin, ModelAdmin):
resource_class = TaskResource resource_class = TaskResource
list_display = ['title', 'content_type', 'content_structure', 'site', 'sector', 'status', 'cluster', 'created_at'] list_display = ['title', 'content_type', 'content_structure', 'site', 'sector', 'status', 'cluster', 'created_at']
list_filter = ['status', 'content_type', 'content_structure', 'site', 'sector', 'cluster'] list_filter = ['status', 'content_type', 'content_structure', 'site', 'sector', 'cluster']
@@ -141,7 +142,7 @@ class TasksAdmin(ExportMixin, SiteSectorAdminMixin, admin.ModelAdmin):
@admin.register(Images) @admin.register(Images)
class ImagesAdmin(SiteSectorAdminMixin, admin.ModelAdmin): class ImagesAdmin(SiteSectorAdminMixin, ModelAdmin):
list_display = ['get_content_title', 'site', 'sector', 'image_type', 'status', 'position', 'created_at'] list_display = ['get_content_title', 'site', 'sector', 'image_type', 'status', 'position', 'created_at']
list_filter = ['image_type', 'status', 'site', 'sector'] list_filter = ['image_type', 'status', 'site', 'sector']
search_fields = ['content__title'] search_fields = ['content__title']
@@ -183,7 +184,7 @@ class ContentResource(resources.ModelResource):
@admin.register(Content) @admin.register(Content)
class ContentAdmin(ExportMixin, SiteSectorAdminMixin, admin.ModelAdmin): class ContentAdmin(ExportMixin, SiteSectorAdminMixin, ModelAdmin):
resource_class = ContentResource resource_class = ContentResource
list_display = ['title', 'content_type', 'content_structure', 'site', 'sector', 'source', 'status', 'get_taxonomy_count', 'created_at'] list_display = ['title', 'content_type', 'content_structure', 'site', 'sector', 'source', 'status', 'get_taxonomy_count', 'created_at']
list_filter = ['content_type', 'content_structure', 'source', 'status', 'site', 'sector', 'created_at'] list_filter = ['content_type', 'content_structure', 'source', 'status', 'site', 'sector', 'created_at']
@@ -326,7 +327,7 @@ class ContentAdmin(ExportMixin, SiteSectorAdminMixin, admin.ModelAdmin):
@admin.register(ContentTaxonomy) @admin.register(ContentTaxonomy)
class ContentTaxonomyAdmin(SiteSectorAdminMixin, admin.ModelAdmin): class ContentTaxonomyAdmin(SiteSectorAdminMixin, ModelAdmin):
list_display = ['name', 'taxonomy_type', 'slug', 'count', 'external_id', 'external_taxonomy', 'site', 'sector'] list_display = ['name', 'taxonomy_type', 'slug', 'count', 'external_id', 'external_taxonomy', 'site', 'sector']
list_filter = ['taxonomy_type', 'site', 'sector'] list_filter = ['taxonomy_type', 'site', 'sector']
search_fields = ['name', 'slug', 'external_taxonomy'] search_fields = ['name', 'slug', 'external_taxonomy']
@@ -357,7 +358,7 @@ class ContentTaxonomyAdmin(SiteSectorAdminMixin, admin.ModelAdmin):
@admin.register(ContentAttribute) @admin.register(ContentAttribute)
class ContentAttributeAdmin(SiteSectorAdminMixin, admin.ModelAdmin): class ContentAttributeAdmin(SiteSectorAdminMixin, ModelAdmin):
list_display = ['name', 'value', 'attribute_type', 'content', 'cluster', 'external_id', 'source', 'site', 'sector'] list_display = ['name', 'value', 'attribute_type', 'content', 'cluster', 'external_id', 'source', 'site', 'sector']
list_filter = ['attribute_type', 'source', 'site', 'sector'] list_filter = ['attribute_type', 'source', 'site', 'sector']
search_fields = ['name', 'value', 'external_attribute_name', 'content__title'] search_fields = ['name', 'value', 'external_attribute_name', 'content__title']
@@ -382,14 +383,14 @@ class ContentAttributeAdmin(SiteSectorAdminMixin, admin.ModelAdmin):
@admin.register(ContentTaxonomyRelation) @admin.register(ContentTaxonomyRelation)
class ContentTaxonomyRelationAdmin(admin.ModelAdmin): class ContentTaxonomyRelationAdmin(ModelAdmin):
list_display = ['content', 'taxonomy', 'created_at'] list_display = ['content', 'taxonomy', 'created_at']
search_fields = ['content__title', 'taxonomy__name'] search_fields = ['content__title', 'taxonomy__name']
readonly_fields = ['created_at', 'updated_at'] readonly_fields = ['created_at', 'updated_at']
@admin.register(ContentClusterMap) @admin.register(ContentClusterMap)
class ContentClusterMapAdmin(SiteSectorAdminMixin, admin.ModelAdmin): class ContentClusterMapAdmin(SiteSectorAdminMixin, ModelAdmin):
list_display = ['content', 'task', 'cluster', 'role', 'source', 'site', 'sector', 'created_at'] list_display = ['content', 'task', 'cluster', 'role', 'source', 'site', 'sector', 'created_at']
list_filter = ['role', 'source', 'site', 'sector'] list_filter = ['role', 'source', 'site', 'sector']
search_fields = ['content__title', 'task__title', 'cluster__name'] search_fields = ['content__title', 'task__title', 'cluster__name']

File diff suppressed because it is too large Load Diff

View File

@@ -1,70 +1,11 @@
{% extends "admin/base.html" %} {% extends "admin/base.html" %}
{% load static %}
{% block title %}{{ title }} | IGNY8 Admin{% endblock %} {% 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 %} {% block branding %}
<h1 id="site-name"> <h1 id="site-name">
<a href="{% url 'admin:index' %}"> <a href="{% url 'admin:index' %}">
<i class="fas fa-rocket"></i> IGNY8 Administration 🚀 IGNY8 Administration
</a> </a>
</h1> </h1>
{% endblock %} {% 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 %}

View File

@@ -1,75 +0,0 @@
{% 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 %}

View File

@@ -1,419 +0,0 @@
{% 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 %}

View File

@@ -1,72 +0,0 @@
{% extends "admin/change_list.html" %}
{% load i18n admin_urls static %}
{% block object-tools-items %}
<li>
<a href="#" class="addlink" onclick="document.getElementById('csv-import-form').style.display='block'; return false;">
Import from CSV
</a>
</li>
{{ block.super }}
{% endblock %}
{% block content %}
<div id="csv-import-form" style="display:none; background: #f8f8f8; padding: 20px; margin: 20px 0; border: 1px solid #ddd;">
<h2>Import Industries from CSV</h2>
<form method="post" enctype="multipart/form-data" action="{% url 'admin_industry_csv_import' %}" id="csv-upload-form">
{% csrf_token %}
<p>
<label for="csv_file">Select CSV file:</label>
<input type="file" name="csv_file" id="csv_file" accept=".csv" required>
</p>
<p>
<strong>CSV Format:</strong> name, description, is_active<br>
<em>Note: Slug will be auto-generated from name</em><br>
<a href="{% url 'admin_industry_csv_template' %}" download>Download template with examples</a>
</p>
<p>
<button type="submit" class="button">Upload and Import</button>
<button type="button" class="button" onclick="document.getElementById('csv-import-form').style.display='none'">Cancel</button>
</p>
</form>
<div id="import-results"></div>
</div>
<script>
document.getElementById('csv-upload-form').addEventListener('submit', function(e) {
e.preventDefault();
var formData = new FormData(this);
var resultsDiv = document.getElementById('import-results');
resultsDiv.innerHTML = '<p>Importing...</p>';
fetch('{% url "admin_industry_csv_import" %}', {
method: 'POST',
body: formData,
headers: {
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
resultsDiv.innerHTML = '<p style="color: green;">✓ Import successful!<br>' +
'Created: ' + data.created + '<br>' +
'Updated: ' + data.updated + '<br>' +
(data.errors.length > 0 ? 'Errors: ' + data.errors.join('<br>') : '') +
'</p>';
setTimeout(function() {
window.location.reload();
}, 2000);
} else {
resultsDiv.innerHTML = '<p style="color: red;">✗ Error: ' + data.error + '</p>';
}
})
.catch(error => {
resultsDiv.innerHTML = '<p style="color: red;">✗ Error: ' + error + '</p>';
});
});
</script>
{{ block.super }}
{% endblock %}

View File

@@ -1,72 +0,0 @@
{% extends "admin/change_list.html" %}
{% load i18n admin_urls static %}
{% block object-tools-items %}
<li>
<a href="#" class="addlink" onclick="document.getElementById('csv-import-form').style.display='block'; return false;">
Import from CSV
</a>
</li>
{{ block.super }}
{% endblock %}
{% block content %}
<div id="csv-import-form" style="display:none; background: #f8f8f8; padding: 20px; margin: 20px 0; border: 1px solid #ddd;">
<h2>Import Industry Sectors from CSV</h2>
<form method="post" enctype="multipart/form-data" action="{% url 'admin_industrysector_csv_import' %}" id="csv-upload-form">
{% csrf_token %}
<p>
<label for="csv_file">Select CSV file:</label>
<input type="file" name="csv_file" id="csv_file" accept=".csv" required>
</p>
<p>
<strong>CSV Format:</strong> name, industry, description, is_active<br>
<em>Note: Use industry name (not slug). Slug will be auto-generated from name</em><br>
<a href="{% url 'admin_industrysector_csv_template' %}" download>Download template with examples</a>
</p>
<p>
<button type="submit" class="button">Upload and Import</button>
<button type="button" class="button" onclick="document.getElementById('csv-import-form').style.display='none'">Cancel</button>
</p>
</form>
<div id="import-results"></div>
</div>
<script>
document.getElementById('csv-upload-form').addEventListener('submit', function(e) {
e.preventDefault();
var formData = new FormData(this);
var resultsDiv = document.getElementById('import-results');
resultsDiv.innerHTML = '<p>Importing...</p>';
fetch('{% url "admin_industrysector_csv_import" %}', {
method: 'POST',
body: formData,
headers: {
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
resultsDiv.innerHTML = '<p style="color: green;">✓ Import successful!<br>' +
'Created: ' + data.created + '<br>' +
'Updated: ' + data.updated + '<br>' +
(data.errors.length > 0 ? 'Errors: ' + data.errors.join('<br>') : '') +
'</p>';
setTimeout(function() {
window.location.reload();
}, 2000);
} else {
resultsDiv.innerHTML = '<p style="color: red;">✗ Error: ' + data.error + '</p>';
}
})
.catch(error => {
resultsDiv.innerHTML = '<p style="color: red;">✗ Error: ' + error + '</p>';
});
});
</script>
{{ block.super }}
{% endblock %}

View File

@@ -1,73 +0,0 @@
{% extends "admin/change_list.html" %}
{% load i18n admin_urls static %}
{% block object-tools-items %}
<li>
<a href="#" class="addlink" onclick="document.getElementById('csv-import-form').style.display='block'; return false;">
Import from CSV
</a>
</li>
{{ block.super }}
{% endblock %}
{% block content %}
<div id="csv-import-form" style="display:none; background: #f8f8f8; padding: 20px; margin: 20px 0; border: 1px solid #ddd;">
<h2>Import Global Keywords from CSV</h2>
<form method="post" enctype="multipart/form-data" action="{% url 'admin_seedkeyword_csv_import' %}" id="csv-upload-form">
{% csrf_token %}
<p>
<label for="csv_file">Select CSV file:</label>
<input type="file" name="csv_file" id="csv_file" accept=".csv" required>
</p>
<p>
<strong>CSV Format:</strong> keyword, industry, sector, volume, difficulty, intent, is_active<br>
<em>Note: Use industry and sector names (not slugs)</em><br>
<strong>Intent values:</strong> Informational, Commercial, Transactional, Navigational<br>
<a href="{% url 'admin_seedkeyword_csv_template' %}" download>Download template with examples</a>
</p>
<p>
<button type="submit" class="button">Upload and Import</button>
<button type="button" class="button" onclick="document.getElementById('csv-import-form').style.display='none'">Cancel</button>
</p>
</form>
<div id="import-results"></div>
</div>
<script>
document.getElementById('csv-upload-form').addEventListener('submit', function(e) {
e.preventDefault();
var formData = new FormData(this);
var resultsDiv = document.getElementById('import-results');
resultsDiv.innerHTML = '<p>Importing...</p>';
fetch('{% url "admin_seedkeyword_csv_import" %}', {
method: 'POST',
body: formData,
headers: {
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
resultsDiv.innerHTML = '<p style="color: green;">✓ Import successful!<br>' +
'Created: ' + data.created + '<br>' +
'Updated: ' + data.updated + '<br>' +
(data.errors.length > 0 ? 'Errors: ' + data.errors.join('<br>') : '') +
'</p>';
setTimeout(function() {
window.location.reload();
}, 2000);
} else {
resultsDiv.innerHTML = '<p style="color: red;">✗ Error: ' + data.error + '</p>';
}
})
.catch(error => {
resultsDiv.innerHTML = '<p style="color: red;">✗ Error: ' + error + '</p>';
});
});
</script>
{{ block.super }}
{% endblock %}