diff --git a/enferno/admin/templates/admin/activity.html b/enferno/admin/templates/admin/activity.html index b4de1a521..b500ee163 100644 --- a/enferno/admin/templates/admin/activity.html +++ b/enferno/admin/templates/admin/activity.html @@ -154,6 +154,7 @@ + @@ -404,6 +405,7 @@ app.component('UniField', UniField); app.component('DualField', DualField); + app.component('Asterisk', Asterisk); app.component('GeoMap', GeoMap); app.component('GeoLocations', GeoLocations); app.component('GlobalMap', GlobalMap); diff --git a/enferno/admin/templates/admin/actors.html b/enferno/admin/templates/admin/actors.html index 18446b052..c08137c60 100644 --- a/enferno/admin/templates/admin/actors.html +++ b/enferno/admin/templates/admin/actors.html @@ -407,6 +407,7 @@ + @@ -1812,6 +1813,7 @@ app.component('EventsSection', EventsSection); app.component('ReadMore', ReadMore); app.component('FieldRenderer', FieldRenderer); + app.component('Asterisk', Asterisk); app.component('OcrTextLayer', OcrTextLayer); app.component('MediaTranscriptionDialog', MediaTranscriptionDialog); diff --git a/enferno/admin/templates/admin/bulletins.html b/enferno/admin/templates/admin/bulletins.html index 0ce557e67..6a18a6eb8 100644 --- a/enferno/admin/templates/admin/bulletins.html +++ b/enferno/admin/templates/admin/bulletins.html @@ -440,6 +440,7 @@ + @@ -1812,6 +1813,7 @@ app.component('IdNumberDynamicField', IdNumberDynamicField); app.component('EventsSection', EventsSection); app.component('FieldRenderer', FieldRenderer); + app.component('Asterisk', Asterisk); app.component('OcrTextLayer', OcrTextLayer); app.component('MediaTranscriptionDialog', MediaTranscriptionDialog); diff --git a/enferno/admin/templates/admin/component-data.html b/enferno/admin/templates/admin/component-data.html index c7fbd8702..a76dfdf6f 100644 --- a/enferno/admin/templates/admin/component-data.html +++ b/enferno/admin/templates/admin/component-data.html @@ -29,7 +29,7 @@

{{ _('Component Data Management') }}

{{ _('Full Location text format') }} + load-endpoint="/admin/api/location-types/?per_page=1000" + :required-fields="['title']" + >
@@ -110,7 +112,7 @@

{{ _('Full Location text format') }} + load-endpoint="/admin/api/countries/?per_page=1000" :required-fields="['title']"> @@ -118,7 +120,7 @@

{{ _('Full Location text format') }} + load-endpoint="/admin/api/ethnographies/?per_page=1000" :required-fields="['title']"> @@ -126,7 +128,7 @@

{{ _('Full Location text format') }} + load-endpoint="/admin/api/dialects/?per_page=1000" :required-fields="['title']"> @@ -146,6 +148,7 @@

{{ _('Full Location text format') }} @@ -154,6 +157,7 @@

{{ _('Full Location text format') }}
@@ -162,6 +166,7 @@

{{ _('Full Location text format') }}
@@ -170,6 +175,7 @@

{{ _('Full Location text format') }}
@@ -178,6 +184,7 @@

{{ _('Full Location text format') }}
@@ -186,6 +193,7 @@

{{ _('Full Location text format') }}
@@ -196,6 +204,7 @@

{{ _('Full Location text format') }}
@@ -205,6 +214,7 @@

{{ _('Full Location text format') }}
@@ -222,6 +232,7 @@

{{ _('Full Location text format') }}
@@ -242,6 +253,7 @@

{{ _('Full Location text format') }}
@@ -250,6 +262,7 @@

{{ _('Full Location text format') }}
@@ -275,6 +288,7 @@

{{ _('Full Location text format') }} {% endif %} + @@ -290,17 +304,12 @@

{{ _('Full Location text format') }} delimiters: delimiters, mixins: [globalMixin], mounted() { - this.setInitialTabStateKey() + // Initialize tabs from URL on first load + this.syncTabsFromHash(); - this.$router.afterEach((to) => { - const [main, child] = to.hash.slice(1).split('/'); - - const mainKey = main && main in this.tabsMap ? main : Object.keys(this.tabsMap)[0]; - const nested = this.tabsMap[mainKey]?.nestedTabsMap || {}; - const childKey = child && child in nested ? child : Object.keys(nested)[0] || null; - - this.tabState.main = mainKey; - this.tabState.child = Object.keys(nested).length ? childKey : null; + // Update tabs when user navigates (back/forward buttons) + this.$router.afterEach(() => { + this.syncTabsFromHash(); }); }, data() { @@ -385,21 +394,16 @@

{{ _('Full Location text format') }} }, deep: true }, - 'tabState.main'(mainKey) { - const nested = this.tabsMap?.[mainKey]?.nestedTabsMap; - - this.tabState.main = mainKey; - this.tabState.child = nested - ? nested[this.tabState.child] - ? this.tabState.child - : Object.keys(nested)[0] - : null; - - this.updateHash(); + 'tabState.main'(newVal, oldVal) { + // Only update hash if change came from user interaction (not from syncTabsFromHash) + if (oldVal !== null) { + this.updateHashFromTabs(); + } }, - - 'tabState.child'() { - this.updateHash(); + 'tabState.child'(newVal, oldVal) { + if (oldVal !== null) { + this.updateHashFromTabs(); + } } }, @@ -422,23 +426,6 @@

{{ _('Full Location text format') }} return [...base, ...(fieldMaps[type] || customFields), { title: "{{ _('Actions') }}", value: 'actions', align: 'end' }]; }, - updateHash() { - const { main, child } = this.tabState; - const hash = [main, child].filter(Boolean).join('/'); - this.$router.push({ name: 'component-data', hash: `#${hash}` }); - }, - setInitialTabStateKey() { - const main = this.tabState.main || Object.keys(this.tabsMap)[0]; - const nested = this.tabsMap[main]?.nestedTabsMap || {}; - const child = this.tabState.child || Object.keys(nested)[0] || null; - - const hash = [main, child].filter(Boolean).join('/'); - - this.$router.replace({ - name: 'component-data', - hash: `#${hash}` - }); - }, onDragEnd() { if (this.isOrderChanged) { this.showLalWarning = true; @@ -475,11 +462,38 @@

{{ _('Full Location text format') }} this.$root.showSnack(response.data); this.showLalWarning = false; }); - } + }, + updateHashFromTabs() { + const { main, child } = this.tabState; + const hash = [main, child].filter(Boolean).join('/'); + + // Only push if hash actually changed + if (this.$route.hash !== `#${hash}`) { + this.$router.push({ + name: 'component-data', + hash: `#${hash}` + }); + } + }, + syncTabsFromHash() { + const hash = this.$route.hash.slice(1); // Remove '#' + const [main, child] = hash ? hash.split('/') : []; + + // Validate and set main tab + const mainKey = main && main in this.tabsMap ? main : Object.keys(this.tabsMap)[0]; + + // Validate and set child tab + const nested = this.tabsMap[mainKey]?.nestedTabsMap || {}; + const childKey = child && child in nested ? child : Object.keys(nested)[0] || null; + + this.tabState.main = mainKey; + this.tabState.child = childKey; + }, } }); app.component('EditableTable', EditableTable); app.component('Draggable', draggable); + app.component('Asterisk', Asterisk); app.use(router).use(vuetify).mount('#app'); {% endblock %} \ No newline at end of file diff --git a/enferno/admin/templates/admin/eventtypes.html b/enferno/admin/templates/admin/eventtypes.html index 0575decd8..ad681e851 100644 --- a/enferno/admin/templates/admin/eventtypes.html +++ b/enferno/admin/templates/admin/eventtypes.html @@ -38,127 +38,79 @@ - ${ formTitle } - - - - - - - - - - - - - - - - - - - - - - - + + + - - - - + + + + + + + - - - - - - - {{ _('Cancel') }} - {{ _('Save') }} - - - - - - - {% if current_user.roles_in(['Admin']) %} - - {% endif %} + + + + - - - {{ _('Import CSV') }} - + + + + - - - - - - - - - - - - - - {{ _('Cancel') }} - {{ ('Save') }} - + + + + + + + + + + + + + + + {{ _('Cancel') }} + {{ _('Save') }} + + - + {% if current_user.roles_in(['Admin']) %} + + {% endif %} + @@ -186,6 +138,8 @@ {% endblock %} {% block js %} + + -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/enferno/admin/templates/admin/incidents.html b/enferno/admin/templates/admin/incidents.html index 047f61920..441950013 100644 --- a/enferno/admin/templates/admin/incidents.html +++ b/enferno/admin/templates/admin/incidents.html @@ -359,6 +359,7 @@ + @@ -1569,6 +1570,7 @@ app.component('RelationEditorCard', RelationEditorCard); app.component('EventsSection', EventsSection); app.component('FieldRenderer', FieldRenderer); + app.component('Asterisk', Asterisk); app.use(router).use(vuetify); router.isReady().then(() => { diff --git a/enferno/admin/templates/admin/jsapi.jinja2 b/enferno/admin/templates/admin/jsapi.jinja2 index 2d250b86e..02dbaef43 100644 --- a/enferno/admin/templates/admin/jsapi.jinja2 +++ b/enferno/admin/templates/admin/jsapi.jinja2 @@ -126,6 +126,7 @@ markFieldAsRequired_: "{{ _('Make the field required') }}", markFieldAsSearchable_: "{{ _('Make the field searchable') }}", showField_: "{{ _('Show Field') }}", hideField_: "{{ _('Hide Field') }}", +requiredFields_: "{{ _('Required fields') }}", showOption_: "{{ _('Show Option') }}", hideOption_: "{{ _('Hide Option') }}", deleteField_: "{{ _('Delete Field') }}", @@ -169,6 +170,7 @@ allUnsavedEditsHaveBeenDiscarded_: "{{ _('All unsaved edits in the form have bee reviewAndConfirmChanges_: "{{ _('Review & Confirm Changes') }}", youreAboutToDeleteAField_: "{{ _('You\'re about to delete a field') }}", fieldsSavedSuccessfully_: "{{ _('Fields saved successfully') }}", +invalidHexColorFormat_: "{{ _('Invalid hex color format (e.g., #F53, #FF5733, or #FF5733FF)') }}", cityListWithMore_: (names, num) => "{{ _('{names} and {num} more') }}".replace('{names}', names).replace('{num}', num), viewEditTranscription_: "{{ _('View/Edit Transcription') }}", transcribe_: "{{ _('Transcribe') }}", @@ -181,6 +183,8 @@ locationOrDateRequired_: "{{ _('Location or Date is required') }}", titleOrTypeRequired_: "{{ _('Title or Event Type is required') }}", preview_ : "{{ _('Preview') }}", pleaseReviewFormForErrors_ : "{{ _('Please review the form for errors.') }}", +importCsv_ : "{{ _('Import CSV') }}", +selectCsvFile_ : "{{ _('Select CSV file') }}", accessGroupsRequired_ : "{{ _('Access Group(s) are required unless unrestricted.') }}", saveActor_: "{{ _('Save Actor') }}", title_: "{{ _('Title') }}", @@ -207,9 +211,10 @@ to_: "{{ _('To') }}", comments_: "{{ _('Comments') }}", commentsAr_: "{{ _('Comments (Ar)') }}", status_: "{{ _('Status') }}", -restrictToAccessGroups_: "{{ _('Restrict Actor to Access Group(s)') }}", +restrictActorToAccessGroups_: "{{ _('Restrict Actor to Access Group(s)') }}", noAccessAccessGroups_: "{{ _('No Access Access Groups') }}", profile_: "{{ _('Profile') }}", +location_: "{{ _('Location') }}", noItemsAvailable_: "{{ _('No items available') }}", noMoreItemsToLoad_: "{{ _('No more items to load') }}", diff --git a/enferno/admin/templates/admin/labels.html b/enferno/admin/templates/admin/labels.html index da646d510..83f374512 100644 --- a/enferno/admin/templates/admin/labels.html +++ b/enferno/admin/templates/admin/labels.html @@ -121,82 +121,84 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - -
{{ _('Available In') }}
- - - + + + + + + + + + + + + + + + + + + + + + + + + + + +
{{ _('Available In') }}
+ + - ${opt.label} -
-
-
- -
- - - {{ _('Cancel') }} - {{ _('Save') }} - -
+ + ${opt.label} + + + + + + + + {{ _('Cancel') }} + {{ _('Save') }} + + +
@@ -217,6 +219,8 @@ {% endblock %} {% block js %} + + -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/enferno/admin/templates/admin/locations.html b/enferno/admin/templates/admin/locations.html index 18f4e62b2..4a3dfc28e 100644 --- a/enferno/admin/templates/admin/locations.html +++ b/enferno/admin/templates/admin/locations.html @@ -53,57 +53,11 @@ {{ _('New Location') }} - {% if current_user.has_role('Admin') %} - - {{ _('Import CSV') }} - - {% endif %} - {% include 'admin/partials/location_dialog.html' %} - - - - - - - {{ _('Import CSV') }} - - - - - - - - - - - - - - - - - {{ _('Cancel') }} - - - {{ _('Save') }} - - - - + {% if current_user.roles_in(['Admin']) %} + + {% endif %} @@ -159,6 +113,8 @@ + + {% if config.GOOGLE_MAPS_API_KEY %} {{ ''|safe }} @@ -418,18 +374,6 @@ }); }, - importCSV() { - const reqData = new FormData(); - reqData.append('csv', this.csvFile) - api.post('/admin/api/location/import/', reqData).then(response => { - - this.imDialog = false; - this.refresh(); - this.showSnack(response.data); - - }) - }, - newItem() { this.editedItem = JSON.parse(JSON.stringify(this.defaultItem)); @@ -517,7 +461,7 @@ }) } }, - + validateForm() { this.$refs.form.validate().then(({ valid, errors }) => { if (valid) { @@ -544,6 +488,8 @@ app.component('GeoMap', GeoMap); app.component('GlobalMap', GlobalMap); app.component('ReadMore', ReadMore); + app.component('Asterisk', Asterisk); + app.component('ImportCsvDialog', ImportCsvDialog); app.use(router).use(vuetify).mount('#app'); {% endblock %} diff --git a/enferno/admin/templates/admin/partials/actor_dialog.html b/enferno/admin/templates/admin/partials/actor_dialog.html index b550aed39..bde57a7e8 100644 --- a/enferno/admin/templates/admin/partials/actor_dialog.html +++ b/enferno/admin/templates/admin/partials/actor_dialog.html @@ -54,29 +54,31 @@ + > + + +

+ > + + +
+ > + + +
+ > + +
+ > + +