diff --git a/apps/account/templates/account/json_editor_field.html b/apps/account/templates/account/json_editor_field.html
index db0c6cc..b8e126d 100644
--- a/apps/account/templates/account/json_editor_field.html
+++ b/apps/account/templates/account/json_editor_field.html
@@ -1,13 +1,12 @@
{% load i18n %}
@@ -429,24 +428,28 @@
align-items: center !important;
justify-content: center !important;
gap: 0.375rem !important;
- background-color: rgb(37, 208, 118) !important; /* Unfold primary-500 */
+ background-color: rgb(37, 208, 118) !important;
+ /* Unfold primary-500 */
color: white !important;
border: none !important;
border-radius: 0.375rem !important;
- padding: 0.5rem 0.875rem !important; /* Smaller padding */
+ padding: 0.5rem 0.875rem !important;
+ /* Smaller padding */
font-weight: 500 !important;
cursor: pointer !important;
transition: all 0.2s ease !important;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08) !important;
margin: 0 0.25rem !important;
- font-size: 0.8125rem !important; /* Smaller font */
+ font-size: 0.8125rem !important;
+ /* Smaller font */
line-height: 1.4 !important;
text-transform: none !important;
letter-spacing: 0.01em !important;
}
.json-editor-btn-modern:hover {
- background-color: rgb(29, 166, 94) !important; /* Unfold primary-600 */
+ background-color: rgb(29, 166, 94) !important;
+ /* Unfold primary-600 */
transform: translateY(-1px) !important;
box-shadow: 0 3px 5px rgba(0, 0, 0, 0.08) !important;
}
@@ -457,7 +460,8 @@
}
/* Button sections styling */
- .json-editor-btngroup, .json-editor-btn-bar {
+ .json-editor-btngroup,
+ .json-editor-btn-bar {
display: flex !important;
flex-wrap: wrap !important;
gap: 0.5rem !important;
@@ -480,29 +484,35 @@
/* Add button styling */
.json-editor-btn-add {
- background-color: rgb(37, 208, 118) !important; /* Unfold primary-500 */
- padding: 0.625rem 1rem !important; /* Smaller padding */
- font-size: 0.875rem !important; /* Smaller font */
+ background-color: rgb(37, 208, 118) !important;
+ /* Unfold primary-500 */
+ padding: 0.625rem 1rem !important;
+ /* Smaller padding */
+ font-size: 0.875rem !important;
+ /* Smaller font */
font-weight: 600 !important;
letter-spacing: 0.01em !important;
border-radius: 0.375rem !important;
- width: auto !important; /* Not full width */
+ width: auto !important;
+ /* Not full width */
margin-bottom: 0.75rem !important;
}
.json-editor-btn-add:hover {
- background-color: rgb(29, 166, 94) !important; /* Unfold primary-600 */
+ background-color: rgb(29, 166, 94) !important;
+ /* Unfold primary-600 */
}
/* Modern HTML5-like error styling */
.je-error-container {
- display: none !important; /* Hide the default error container */
+ display: none !important;
+ /* Hide the default error container */
}
/* Style for invalid inputs */
- .je-error + input,
- .je-error + select,
- .je-error + textarea,
+ .je-error+input,
+ .je-error+select,
+ .je-error+textarea,
.has-error .modern-form-control {
border-color: rgb(239, 68, 68) !important;
padding-right: 2.5rem !important;
@@ -545,10 +555,10 @@
}
/* Show tooltip on hover over the input */
- .je-error + input:hover + .je-error::after,
- .je-error + select:hover + .je-error::after,
- .je-error + textarea:hover + .je-error::after,
- .has-error .modern-form-control:hover + .je-error::after {
+ .je-error+input:hover+.je-error::after,
+ .je-error+select:hover+.je-error::after,
+ .je-error+textarea:hover+.je-error::after,
+ .has-error .modern-form-control:hover+.je-error::after {
visibility: visible !important;
opacity: 1 !important;
}
@@ -567,21 +577,23 @@
transition: opacity 0.3s !important;
}
- .je-error + input:hover + .je-error::before,
- .je-error + select:hover + .je-error::before,
- .je-error + textarea:hover + .je-error::before,
- .has-error .modern-form-control:hover + .je-error::before {
+ .je-error+input:hover+.je-error::before,
+ .je-error+select:hover+.je-error::before,
+ .je-error+textarea:hover+.je-error::before,
+ .has-error .modern-form-control:hover+.je-error::before {
visibility: visible !important;
opacity: 1 !important;
}
/* Delete button styling */
.json-editor-btn-delete {
- background-color: rgb(1, 53, 59) !important; /* Unfold secondary-500 */
+ background-color: rgb(1, 53, 59) !important;
+ /* Unfold secondary-500 */
}
.json-editor-btn-delete:hover {
- background-color: rgb(1, 43, 48) !important; /* Unfold secondary-600 */
+ background-color: rgb(1, 43, 48) !important;
+ /* Unfold secondary-600 */
}
/* Hide Delete Last buttons */
@@ -590,29 +602,36 @@
}
/* Move up/down buttons */
- .json-editor-btntype-moveup, .json-editor-btntype-movedown {
+ .json-editor-btntype-moveup,
+ .json-editor-btntype-movedown {
background-color: rgba(1, 53, 59, 0.8) !important;
}
- .json-editor-btntype-moveup:hover, .json-editor-btntype-movedown:hover {
+ .json-editor-btntype-moveup:hover,
+ .json-editor-btntype-movedown:hover {
background-color: rgb(1, 53, 59) !important;
}
/* Button sections styling */
- .json-editor-btngroup, .json-editor-btn-bar {
+ .json-editor-btngroup,
+ .json-editor-btn-bar {
display: flex !important;
flex-wrap: wrap !important;
gap: 0.375rem !important;
margin-top: 1rem !important;
padding: 0.625rem !important;
- background-color: rgba(1, 53, 59, 0.03) !important; /* More subtle background */
- border: 1px solid rgba(1, 53, 59, 0.08) !important; /* Subtle border */
+ background-color: rgba(1, 53, 59, 0.03) !important;
+ /* More subtle background */
+ border: 1px solid rgba(1, 53, 59, 0.08) !important;
+ /* Subtle border */
border-radius: 0.5rem !important;
visibility: visible !important;
width: 100% !important;
justify-content: flex-end !important;
- order: 999 !important; /* Ensure it appears at the bottom */
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.02) !important; /* Subtle shadow */
+ order: 999 !important;
+ /* Ensure it appears at the bottom */
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.02) !important;
+ /* Subtle shadow */
}
/* Modern button group styling */
@@ -649,7 +668,8 @@
}
/* Form labels */
- label, .je-label {
+ label,
+ .je-label {
font-size: 0.875rem !important;
font-weight: 500 !important;
color: rgb(1, 53, 59) !important;
@@ -675,7 +695,8 @@
}
/* Labels */
- label, .je-label {
+ label,
+ .je-label {
display: block !important;
margin-bottom: 0.5rem !important;
font-weight: 500 !important;
@@ -684,7 +705,8 @@
}
/* Form groups */
- .form-group, .je-object__container {
+ .form-group,
+ .je-object__container {
margin-bottom: 1.5rem !important;
width: 100% !important;
}
@@ -714,31 +736,39 @@
/* Dark mode support - Using Unfold color scheme */
@media (prefers-color-scheme: dark) {
.json-editor-container {
- background-color: rgb(1, 43, 48) !important; /* Unfold secondary-600 */
+ background-color: rgb(1, 43, 48) !important;
+ /* Unfold secondary-600 */
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
}
.json-view-editor {
- background-color: rgb(1, 43, 48) !important; /* Unfold secondary-600 */
+ background-color: rgb(1, 43, 48) !important;
+ /* Unfold secondary-600 */
}
.table-responsive {
- border-color: rgb(1, 36, 40) !important; /* Unfold secondary-700 */
- background-color: rgb(1, 43, 48) !important; /* Unfold secondary-600 */
+ border-color: rgb(1, 36, 40) !important;
+ /* Unfold secondary-700 */
+ background-color: rgb(1, 43, 48) !important;
+ /* Unfold secondary-600 */
}
.modern-table-header {
- background-color: rgb(1, 30, 34) !important; /* Unfold secondary-800 */
+ background-color: rgb(1, 30, 34) !important;
+ /* Unfold secondary-800 */
color: white !important;
- border-bottom-color: rgb(0, 26, 29) !important; /* Unfold secondary-900 */
+ border-bottom-color: rgb(0, 26, 29) !important;
+ /* Unfold secondary-900 */
}
.modern-table-row {
- border-bottom-color: rgb(1, 30, 34) !important; /* Unfold secondary-800 */
+ border-bottom-color: rgb(1, 30, 34) !important;
+ /* Unfold secondary-800 */
}
.modern-table-row:hover {
- background-color: rgb(1, 36, 40) !important; /* Unfold secondary-700 */
+ background-color: rgb(1, 36, 40) !important;
+ /* Unfold secondary-700 */
}
.modern-table-row td {
@@ -746,38 +776,47 @@
}
.modern-form-control {
- background-color: rgb(1, 36, 40) !important; /* Unfold secondary-700 */
- border-color: rgb(1, 30, 34) !important; /* Unfold secondary-800 */
+ background-color: rgb(1, 36, 40) !important;
+ /* Unfold secondary-700 */
+ border-color: rgb(1, 30, 34) !important;
+ /* Unfold secondary-800 */
color: white !important;
}
- label, .je-label {
+ label,
+ .je-label {
color: white !important;
}
/* Button sections in dark mode */
- .json-editor-btngroup, .json-editor-btn-bar {
- background-color: rgba(1, 30, 34, 0.25) !important; /* More subtle dark background */
- border: 1px solid rgba(37, 208, 118, 0.1) !important; /* Subtle primary color border */
+ .json-editor-btngroup,
+ .json-editor-btn-bar {
+ background-color: rgba(1, 30, 34, 0.25) !important;
+ /* More subtle dark background */
+ border: 1px solid rgba(37, 208, 118, 0.1) !important;
+ /* Subtle primary color border */
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) !important;
}
.modern-form-control:focus {
- border-color: rgb(37, 208, 118) !important; /* Unfold primary-500 */
+ border-color: rgb(37, 208, 118) !important;
+ /* Unfold primary-500 */
box-shadow: 0 0 0 3px rgba(37, 208, 118, 0.2) !important;
}
/* Error styling in dark mode */
- .je-error + input,
- .je-error + select,
- .je-error + textarea,
+ .je-error+input,
+ .je-error+select,
+ .je-error+textarea,
.has-error .modern-form-control {
- border-color: rgb(252, 165, 165) !important; /* Lighter red for dark mode */
+ border-color: rgb(252, 165, 165) !important;
+ /* Lighter red for dark mode */
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23fca5a5' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E");
}
.je-error::after {
- background-color: rgb(185, 28, 28) !important; /* Darker red background for tooltip */
+ background-color: rgb(185, 28, 28) !important;
+ /* Darker red background for tooltip */
color: white !important;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.25) !important;
}
@@ -788,7 +827,8 @@
/* Card styling in dark mode */
.modern-card {
- background-color: rgb(1, 43, 48) !important; /* Unfold secondary-600 */
+ background-color: rgb(1, 43, 48) !important;
+ /* Unfold secondary-600 */
}
/* Error messages in dark mode */
@@ -796,5 +836,4 @@
color: #f87171 !important;
}
}
-
-
+
\ No newline at end of file
diff --git a/apps/hadis/admin/hadis.py b/apps/hadis/admin/hadis.py
index 8bbd170..ec9191f 100644
--- a/apps/hadis/admin/hadis.py
+++ b/apps/hadis/admin/hadis.py
@@ -589,8 +589,9 @@ class ReferenceImageInline(TabularInline):
class HadisReferenceInline(TabularInline):
"""Inline for HadisReference in Hadis admin"""
model = HadisReference
+ form = HadisReferenceAdminForm
extra = 0
- fields = ('book_reference',)
+ fields = ('book_reference', 'description')
readonly_fields = ('created_at',)
diff --git a/apps/hadis/admin/reference.py b/apps/hadis/admin/reference.py
index a082f77..41d5940 100644
--- a/apps/hadis/admin/reference.py
+++ b/apps/hadis/admin/reference.py
@@ -415,6 +415,7 @@ class BookAttributeInline(TabularInline):
Inline for managing Book Attributes (Key-Value pairs) inside BookReference.
"""
model = BookAttribute
+ form = BookAttributeAdminForm
extra = 0
fields = ('title', 'value')
readonly_fields = ('created_at',)
diff --git a/apps/hadis/admin/transmitter.py b/apps/hadis/admin/transmitter.py
index fa9f751..c2b90e4 100644
--- a/apps/hadis/admin/transmitter.py
+++ b/apps/hadis/admin/transmitter.py
@@ -20,18 +20,7 @@ class HadisTransmitterInline(TabularInline):
fields = ('hadis', 'order')
-class TransmitterOpinionInline(TabularInline):
- """Inline for TransmitterOpinion in Transmitters admin"""
- model = TransmitterOpinion
- extra = 0
- fields = ('scholar_name', 'status')
-
-
-class TransmitterOriginalTextInline(TabularInline):
- """Inline for TransmitterOriginalText in Transmitters admin"""
- model = TransmitterOriginalText
- extra = 0
- fields = ('title', 'slug')
+# (TransmitterOpinionInline and TransmitterOriginalTextInline moved after their forms)
# Custom Forms for JSON Fields
@@ -123,63 +112,7 @@ class TransmittersAdminForm(forms.ModelForm):
})
-class TransmittersAdmin(ModelAdmin):
- """Admin for Transmitters model"""
- form = TransmittersAdminForm
- list_display = ('display_header', 'birth_year_hijri', 'death_year_hijri')
- list_filter = ('birth_year_hijri', 'death_year_hijri')
- search_fields = ('full_name', 'description')
- readonly_fields = ('created_at', 'updated_at')
- inlines = [HadisTransmitterInline, TransmitterOpinionInline, TransmitterOriginalTextInline]
-
- fieldsets = (
- (None, {
- 'fields': ('full_name', 'birth_year_hijri', 'death_year_hijri','known_as','nickname')
- }),
- (_('Additional Information'), {
- 'fields': ('description','origin','lived_in','died_in','kunya'),
- # 'classes': ('collapse',)
- }),
- (_('Timestamps'), {
- 'fields': ('created_at', 'updated_at'),
- # 'classes': ('collapse',)
- }),
- )
- @display(description=_('Full Name'), ordering='full_name')
- def get_full_name_display(self, obj):
- """
- Parses the JSON full_name and returns the first item's text.
- """
- if obj.full_name and isinstance(obj.full_name, list) and len(obj.full_name) > 0:
- # Safely get the first item
- first_item = obj.full_name[0]
- if isinstance(first_item, dict):
- return first_item.get('text', '-')
- return '-'
-
- @display(description=_("Transmitter"), header=True)
- def display_header(self, obj):
-
- return self.get_full_name_display(obj),
-
-
-class HadisTransmitterAdmin(ModelAdmin):
- """Admin for HadisTransmitter model"""
- list_display = ('hadis', 'transmitter', 'order', 'created_at')
- list_filter = ( 'created_at',)
- search_fields = ('hadis__title', 'transmitter__full_name')
- readonly_fields = ('created_at',)
- ordering = ('hadis', 'order')
-
- fieldsets = (
- (None, {
- 'fields': ('hadis', 'transmitter', 'order')
- }),
- (_('Timestamps'), {
- 'fields': ('created_at',),
- 'classes': ('collapse',)
- }),
- )
+# (Admins moved down to avoid NameError)
# Custom Forms for JSON Fields
@@ -513,6 +446,84 @@ class TransmitterOriginalTextAdminForm(forms.ModelForm):
})
+class TransmitterOpinionInline(TabularInline):
+ """Inline for TransmitterOpinion in Transmitters admin"""
+ model = TransmitterOpinion
+ form = TransmitterOpinionAdminForm
+ extra = 0
+ fields = ('scholar_name', 'opinion_text', 'status')
+ def has_add_permission(self, request, obj=None):
+ return False
+
+class TransmitterOriginalTextInline(TabularInline):
+ """Inline for TransmitterOriginalText in Transmitters admin"""
+ model = TransmitterOriginalText
+ form = TransmitterOriginalTextAdminForm
+ extra = 0
+ fields = ('title', 'text', 'translation', 'slug')
+ def has_add_permission(self, request, obj=None):
+ return False
+
+
+class TransmittersAdmin(ModelAdmin):
+ """Admin for Transmitters model"""
+ form = TransmittersAdminForm
+ list_display = ('display_header', 'birth_year_hijri', 'death_year_hijri')
+ list_filter = ('birth_year_hijri', 'death_year_hijri')
+ search_fields = ('full_name', 'description')
+ readonly_fields = ('created_at', 'updated_at')
+ inlines = [HadisTransmitterInline, TransmitterOpinionInline, TransmitterOriginalTextInline]
+
+ fieldsets = (
+ (None, {
+ 'fields': ('full_name', 'birth_year_hijri', 'death_year_hijri','known_as','nickname')
+ }),
+ (_('Additional Information'), {
+ 'fields': ('description','origin','lived_in','died_in','kunya'),
+ # 'classes': ('collapse',)
+ }),
+ (_('Timestamps'), {
+ 'fields': ('created_at', 'updated_at'),
+ # 'classes': ('collapse',)
+ }),
+ )
+ @display(description=_('Full Name'), ordering='full_name')
+ def get_full_name_display(self, obj):
+ """
+ Parses the JSON full_name and returns the first item's text.
+ """
+ if obj.full_name and isinstance(obj.full_name, list) and len(obj.full_name) > 0:
+ # Safely get the first item
+ first_item = obj.full_name[0]
+ if isinstance(first_item, dict):
+ return first_item.get('text', '-')
+ return '-'
+
+ @display(description=_("Transmitter"), header=True)
+ def display_header(self, obj):
+
+ return self.get_full_name_display(obj),
+
+
+class HadisTransmitterAdmin(ModelAdmin):
+ """Admin for HadisTransmitter model"""
+ list_display = ('hadis', 'transmitter', 'order', 'created_at')
+ list_filter = ( 'created_at',)
+ search_fields = ('hadis__title', 'transmitter__full_name')
+ readonly_fields = ('created_at',)
+ ordering = ('hadis', 'order')
+
+ fieldsets = (
+ (None, {
+ 'fields': ('hadis', 'transmitter', 'order')
+ }),
+ (_('Timestamps'), {
+ 'fields': ('created_at',),
+ 'classes': ('collapse',)
+ }),
+ )
+
+
# Main Admin Classes
class NarratorLayerAdmin(ModelAdmin):
"""Admin for NarratorLayer model"""