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"""