diff --git a/ghost/admin/app/components/modal-add-label-members.hbs b/ghost/admin/app/components/modal-add-label-members.hbs deleted file mode 100644 index 5c5295432a..0000000000 --- a/ghost/admin/app/components/modal-add-label-members.hbs +++ /dev/null @@ -1,65 +0,0 @@ - -{{svg-jar "close"}} - -{{#if this.confirmed}} -
- {{#if this.error}} -
- {{svg-jar "warning" class="w4 h4 fill-red mr2 nudge-top--3"}} -
-

- {{this.error}} -

-
-
- {{else}} -
- {{svg-jar "check-circle" class="w4 h4 stroke-green mr2"}} -

- Label added to {{gh-pluralize this.response.stats.successful "member"}} - successfully -

-
- {{#if this.response.stats.unsuccessful}} -
- {{svg-jar "warning" class="w4 h4 fill-red mr2 nudge-top--3"}} -
-

- Failed to add label to {{gh-pluralize this.response.stats.unsuccessful "member"}} -

-
-
- {{/if}} - {{/if}} -
-{{else}} - -{{/if}} - - diff --git a/ghost/admin/app/components/modal-add-label-members.js b/ghost/admin/app/components/modal-add-label-members.js deleted file mode 100644 index 0b3d8ce759..0000000000 --- a/ghost/admin/app/components/modal-add-label-members.js +++ /dev/null @@ -1,38 +0,0 @@ -import ModalComponent from 'ghost-admin/components/modal-base'; -import {alias, not} from '@ember/object/computed'; -import {inject as service} from '@ember/service'; -import {task} from 'ember-concurrency'; - -export default ModalComponent.extend({ - membersStats: service(), - selectedLabel: null, - // Allowed actions - confirm: () => {}, - - isDisabled: not('selectedLabel'), - member: alias('model'), - - actions: { - confirm() { - this.addLabelTask.perform(); - }, - - setLabel(label) { - this.set('selectedLabel', label); - } - }, - - addLabelTask: task(function* () { - try { - const response = yield this.confirm(this.selectedLabel); - this.set('response', response); - this.set('confirmed', true); - } catch (e) { - if (e.payload?.errors) { - this.set('confirmed', true); - this.set('error', e.payload.errors[0].message); - } - throw e; - } - }).drop() -}); diff --git a/ghost/admin/app/components/modals/members/bulk-add-label.hbs b/ghost/admin/app/components/modals/members/bulk-add-label.hbs new file mode 100644 index 0000000000..87eee95f02 --- /dev/null +++ b/ghost/admin/app/components/modals/members/bulk-add-label.hbs @@ -0,0 +1,81 @@ + \ No newline at end of file diff --git a/ghost/admin/app/components/modals/members/bulk-add-label.js b/ghost/admin/app/components/modals/members/bulk-add-label.js new file mode 100644 index 0000000000..26d486908b --- /dev/null +++ b/ghost/admin/app/components/modals/members/bulk-add-label.js @@ -0,0 +1,60 @@ +import Component from '@glimmer/component'; +import {action} from '@ember/object'; +import {inject as service} from '@ember/service'; +import {task} from 'ember-concurrency'; +import {tracked} from '@glimmer/tracking'; + +export default class MembersBulkAddLabelModal extends Component { + @service ajax; + @service ghostPaths; + + @tracked error; + @tracked response; + @tracked selectedLabel; + + get isDisabled() { + return !this.args.data.query || !this.selectedLabel; + } + + get hasRun() { + return !!(this.error || this.response); + } + + @action + setLabel(label) { + this.selectedLabel = label; + } + + @task({drop: true}) + *addLabelTask() { + try { + const query = new URLSearchParams(this.args.data.query); + const addLabelUrl = `${this.ghostPaths.url.api('members/bulk')}?${query}`; + const response = yield this.ajax.put(addLabelUrl, { + data: { + bulk: { + action: 'addLabel', + meta: { + label: { + id: this.selectedLabel + } + } + } + } + }); + + this.args.data.onComplete?.(); + + this.response = response?.bulk?.meta; + + return true; + } catch (e) { + if (e.payload?.errors) { + this.error = e.payload.errors[0].message; + } else { + this.error = 'An unknown error occurred. Please try again.'; + } + throw e; + } + } +} diff --git a/ghost/admin/app/controllers/members.js b/ghost/admin/app/controllers/members.js index f52e53fe25..9fe29ee658 100644 --- a/ghost/admin/app/controllers/members.js +++ b/ghost/admin/app/controllers/members.js @@ -30,6 +30,7 @@ export default class MembersController extends Controller { @service feature; @service ghostPaths; @service membersStats; + @service modals; @service router; @service store; @service utils; @@ -55,7 +56,6 @@ export default class MembersController extends Controller { @tracked showLabelModal = false; @tracked showDeleteMembersModal = false; @tracked showUnsubscribeMembersModal = false; - @tracked showAddMembersLabelModal = false; @tracked showRemoveMembersLabelModal = false; @tracked filters = A([]); @tracked softFilters = A([]); @@ -311,6 +311,18 @@ export default class MembersController extends Controller { this.showLabelModal = !this.showLabelModal; } + @action + bulkAddLabel() { + this.modals.open('modals/members/bulk-add-label', { + query: this.getApiQueryObject(), + onComplete: () => { + // reset and reload + this.store.unloadAll('member'); + this.reload(); + } + }); + } + @action changePaidParam(paid) { this.paidParam = paid.value; @@ -326,11 +338,6 @@ export default class MembersController extends Controller { this.showUnsubscribeMembersModal = !this.showUnsubscribeMembersModal; } - @action - toggleAddMembersLabelModal() { - this.showAddMembersLabelModal = !this.showAddMembersLabelModal; - } - @action toggleRemoveMembersLabelModal() { this.showRemoveMembersLabelModal = !this.showRemoveMembersLabelModal; @@ -346,11 +353,6 @@ export default class MembersController extends Controller { return this.unsubscribeMembersTask.perform(); } - @action - addLabelToMembers(selectedLabel) { - return this.addLabelToMembersTask.perform(selectedLabel); - } - @action removeLabelFromMembers(selectedLabel) { return this.removeLabelFromMembersTask.perform(selectedLabel); @@ -489,30 +491,6 @@ export default class MembersController extends Controller { return response?.bulk?.meta; } - @task({drop: true}) - *addLabelToMembersTask(selectedLabel) { - const query = new URLSearchParams(this.getApiQueryObject()); - const addLabelUrl = `${this.ghostPaths.url.api('members/bulk')}?${query}`; - const response = yield this.ajax.put(addLabelUrl, { - data: { - bulk: { - action: 'addLabel', - meta: { - label: { - id: selectedLabel - } - } - } - } - }); - - // reset and reload - this.store.unloadAll('member'); - this.reload(); - - return response?.bulk?.meta; - } - @task({drop: true}) *removeLabelFromMembersTask(selectedLabel) { const query = new URLSearchParams(this.getApiQueryObject()); diff --git a/ghost/admin/app/helpers/members-count-fetcher.js b/ghost/admin/app/helpers/members-count-fetcher.js new file mode 100644 index 0000000000..a043282466 --- /dev/null +++ b/ghost/admin/app/helpers/members-count-fetcher.js @@ -0,0 +1,39 @@ +import {Resource} from 'ember-could-get-used-to-this'; +import {inject as service} from '@ember/service'; +import {task} from 'ember-concurrency'; +import {tracked} from '@glimmer/tracking'; + +export default class MembersCount extends Resource { + @service store; + + @tracked count = null; + + get value() { + return { + isLoading: this.fetchMembersTask.isRunning, + count: this.count + }; + } + + setup() { + const query = this.args.named.query || {}; + this._query = query; + this.fetchMembersTask.perform({query}); + } + + update() { + // required due to a weird invalidation issue when using Ember Data with ember-could-get-used-to-this + // TODO: re-test after upgrading to ember-resources + if (this.args.named.query !== this._query) { + const query = this.args.named.query || {}; + this._query = query; + this.fetchMembersTask.perform({query}); + } + } + + @task + *fetchMembersTask({query} = {}) { + const result = yield this.store.query('member', {...query, limit: 1}); + this.count = result.meta.pagination.total; + } +} diff --git a/ghost/admin/app/templates/members.hbs b/ghost/admin/app/templates/members.hbs index 5d6bc3fcac..af9cf792d9 100644 --- a/ghost/admin/app/templates/members.hbs +++ b/ghost/admin/app/templates/members.hbs @@ -69,7 +69,7 @@ {{#if (and this.members.length this.isFiltered)}}
  • -
  • @@ -178,16 +178,6 @@ {{outlet}} -{{#if this.showAddMembersLabelModal}} - -{{/if}} - {{#if this.showRemoveMembersLabelModal}}