diff --git a/ghost/admin/app/components/gh-member-label-input.hbs b/ghost/admin/app/components/gh-member-label-input.hbs index 1f9e499767..acf4c20982 100644 --- a/ghost/admin/app/components/gh-member-label-input.hbs +++ b/ghost/admin/app/components/gh-member-label-input.hbs @@ -2,11 +2,12 @@ @extra={{hash tokenComponent="gh-token-input/label-token" }} - @onChange={{action "updateLabels"}} - @onCreate={{action "createLabel"}} + @onChange={{this.updateLabels}} + @onCreate={{this.createLabel}} @options={{this.availableLabels}} @renderInPlace={{true}} - @selected={{selectedLabels}} - @showCreateWhen={{action "hideCreateOptionOnMatchingLabel"}} + @selected={{this.selectedLabels}} + @showCreateWhen={{this.hideCreateOptionOnMatchingLabel}} @triggerId={{this.triggerId}} + @disabled={{@disabled}} /> diff --git a/ghost/admin/app/components/gh-member-label-input.js b/ghost/admin/app/components/gh-member-label-input.js index 14c507fa41..3296a1b529 100644 --- a/ghost/admin/app/components/gh-member-label-input.js +++ b/ghost/admin/app/components/gh-member-label-input.js @@ -1,100 +1,91 @@ -import Component from '@ember/component'; -import {computed} from '@ember/object'; +import Component from '@glimmer/component'; +import {action} from '@ember/object'; import {inject as service} from '@ember/service'; -import {sort} from '@ember/object/computed'; +import {tracked} from '@glimmer/tracking'; -export default Component.extend({ +export default class GhMemberLabelInput extends Component { + @service + store; - store: service(), + @tracked + selectedLabels = []; - // public attrs - member: null, - labelName: '', + get availableLabels() { + return this._availableLabels.toArray().sort((labelA, labelB) => { + return labelA.name.localeCompare(labelB.name, undefined, {ignorePunctuation: true}); + }); + } - // internal attrs - _availableLabels: null, - - selectedLabels: computed.reads('member.labels'), - - availableLabels: sort('_availableLabels.[]', function (labelA, labelB) { - // ignorePunctuation means the # in label names is ignored - return labelA.name.localeCompare(labelB.name, undefined, {ignorePunctuation: true}); - }), - - availableLabelNames: computed('availableLabels.@each.name', function () { - return this.availableLabels.map(label => label.name.toLowerCase()); - }), - - init() { - this._super(...arguments); + constructor(...args) { + super(...args); // perform a background query to fetch all users and set `availableLabels` // to a live-query that will be immediately populated with what's in the // store and be updated when the above query returns this.store.query('label', {limit: 'all'}); - this.set('_availableLabels', this.store.peekAll('label')); - }, + this._availableLabels = this.store.peekAll('label'); + this.selectedLabels = this.args.labels || []; + } - willDestroyElement() { - // NOTE: cleans up labels store in case they were not persisted, this avoids unsaved labels - // from appearing on different input instances when unsaved - this.get('_availableLabels').forEach((label) => { + get availableLabelNames() { + return this.availableLabels.map(label => label.name.toLowerCase()); + } + + willDestroy() { + this._availableLabels.forEach((label) => { if (label.get('isNew')) { this.store.deleteRecord(label); } }); - }, + } - actions: { - matchLabels(labelName, term) { - return labelName.toLowerCase() === term.trim().toLowerCase(); - }, + @action + hideCreateOptionOnMatchingLabel(term) { + return !this.availableLabelNames.includes(term.toLowerCase()); + } - hideCreateOptionOnMatchingLabel(term) { - return !this.availableLabelNames.includes(term.toLowerCase()); - }, + @action + updateLabels(newLabels) { + let currentLabels = this.selectedLabels; - updateLabels(newLabels) { - let currentLabels = this.get('member.labels'); - - // destroy new+unsaved labels that are no longer selected - currentLabels.forEach(function (label) { - if (!newLabels.includes(label) && label.get('isNew')) { - label.destroyRecord(); - } - }); - - // update labels - return this.set('member.labels', newLabels); - }, - - createLabel(labelName) { - let currentLabels = this.get('member.labels'); - let currentLabelNames = currentLabels.map(label => label.get('name').toLowerCase()); - let labelToAdd; - - labelName = labelName.trim(); - - // abort if label is already selected - if (currentLabelNames.includes(labelName.toLowerCase())) { - return; + // destroy new+unsaved labels that are no longer selected + currentLabels.forEach(function (label) { + if (!newLabels.includes(label) && label.get('isNew')) { + label.destroyRecord(); } + }); - // find existing label if there is one - labelToAdd = this._findLabelByName(labelName); + // update labels + this.selectedLabels = newLabels; + this.args.onChange(newLabels); + } - // create new label if no match - if (!labelToAdd) { - labelToAdd = this.store.createRecord('label', { - name: labelName - }); - } + @action + createLabel(labelName) { + let currentLabels = this.selectedLabels; + let currentLabelNames = currentLabels.map(label => label.get('name').toLowerCase()); + let labelToAdd; - // push label onto member relationship - return currentLabels.pushObject(labelToAdd); + labelName = labelName.trim(); + + // abort if label is already selected + if (currentLabelNames.includes(labelName.toLowerCase())) { + return; } - }, - // methods + // find existing label if there is one + labelToAdd = this._findLabelByName(labelName); + + // create new label if no match + if (!labelToAdd) { + labelToAdd = this.store.createRecord('label', { + name: labelName + }); + } + + // push label onto member relationship + currentLabels.pushObject(labelToAdd); + this.args.onChange(currentLabels); + } _findLabelByName(name) { let withMatchingName = function (label) { @@ -102,4 +93,4 @@ export default Component.extend({ }; return this.availableLabels.find(withMatchingName); } -}); +} diff --git a/ghost/admin/app/components/gh-member-settings-form.hbs b/ghost/admin/app/components/gh-member-settings-form.hbs index 518ea35fe8..4393edd90f 100644 --- a/ghost/admin/app/components/gh-member-settings-form.hbs +++ b/ghost/admin/app/components/gh-member-settings-form.hbs @@ -101,7 +101,7 @@
The CSV contains errors! {{unless this.importDisabled "Some members will not be imported."}}
- {{#if validationErrors}} -The import contains errors!
-Your import is being processed, and you’ll receive a confirmation email as soon as it’s complete. Usually this only takes a few minutes, but larger imports may take longer.
+A total of {{format-number this.importResponse.importedCount}} {{gh-pluralize this.importResponse.importedCount 'person' without-count=true}} were successfully added or updated in your list of members, and now have access to your site.
+{{if (eq this.importResponse.importedCount 0) "No members were added. "}}{{format-number this.importResponse.errorCount}} {{gh-pluralize this.importResponse.errorCount "member" without-count=true}} {{if (eq this.importResponse.errorCount 1) "was" "were"}} skipped due to the following errors:
+{{error.message}} ({{error.count}})
+ {{/each}} +{{gh-pluralize this.importResponse.imported.count "Member" without-count=true}} added
-{{gh-pluralize this.importResponse.invalid.count "Error" without-count=true}}
-{{format-number this.importResponse.invalid.count}} {{gh-pluralize this.importResponse.invalid.count "member" without-count=true}} were skipped due to the following errors:
-{{this.failureMessage}}
+{{this.errorMessage}}
{{this.error.message}}
+ {{/if}} +If an email address in your CSV matches an existing member, they will be updated with the mapped values.
+ +{{this.error.message}}
+