Feature: Mass award badge (#8694)
* UI: Mass grant a badge from the admin ui * Send the uploaded CSV and badge ID to the backend * Read the CSV and grant badge in batches * UX: Communicate the result to the user * Don't award if badge is disabled * Create a 'send_notification' method to remove duplicated code, slightly shrink badge image. Replace router transition with href. * Dynamically discover current route
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
import Controller from "@ember/controller";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
|
||||
export default Controller.extend({
|
||||
saving: false,
|
||||
|
||||
actions: {
|
||||
massAward() {
|
||||
const file = document.querySelector("#massAwardCSVUpload").files[0];
|
||||
|
||||
if (this.model && file) {
|
||||
const options = {
|
||||
type: "POST",
|
||||
processData: false,
|
||||
contentType: false,
|
||||
data: new FormData()
|
||||
};
|
||||
|
||||
options.data.append("file", file);
|
||||
|
||||
this.set("saving", true);
|
||||
|
||||
ajax(`/admin/badges/award/${this.model.id}`, options)
|
||||
.then(() => {
|
||||
bootbox.alert(I18n.t("admin.badges.mass_award.success"));
|
||||
})
|
||||
.catch(popupAjaxError)
|
||||
.finally(() => this.set("saving", false));
|
||||
} else {
|
||||
bootbox.alert(I18n.t("admin.badges.mass_award.aborted"));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1,2 +1,18 @@
|
||||
import Controller from "@ember/controller";
|
||||
export default Controller.extend();
|
||||
import { inject as service } from "@ember/service";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
|
||||
export default Controller.extend({
|
||||
routing: service("-routing"),
|
||||
|
||||
@discourseComputed("routing.currentRouteName")
|
||||
selectedRoute() {
|
||||
const currentRoute = this.routing.currentRouteName;
|
||||
const indexRoute = "adminBadges.index";
|
||||
if (currentRoute === indexRoute) {
|
||||
return "adminBadges.show";
|
||||
} else {
|
||||
return this.routing.currentRouteName;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import Route from "discourse/routes/discourse";
|
||||
|
||||
export default Route.extend({
|
||||
model(params) {
|
||||
if (params.badge_id !== "new") {
|
||||
return this.modelFor("adminBadges").findBy(
|
||||
"id",
|
||||
parseInt(params.badge_id, 10)
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -190,6 +190,7 @@ export default function() {
|
||||
"adminBadges",
|
||||
{ path: "/badges", resetNamespace: true },
|
||||
function() {
|
||||
this.route("award", { path: "/award/:badge_id" });
|
||||
this.route("show", { path: "/:badge_id" });
|
||||
}
|
||||
);
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
{{#d-section class="award-badge"}}
|
||||
<form class="form-horizontal">
|
||||
<h1>{{i18n 'admin.badges.mass_award.title'}}</h1>
|
||||
<div class='badge-preview'>
|
||||
{{#if model}}
|
||||
{{icon-or-image model}}
|
||||
<span class="badge-display-name">{{model.name}}</span>
|
||||
{{else}}
|
||||
<span class='badge-placeholder'>{{I18n 'admin.badges.mass_award.no_badge_selected'}}</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div>
|
||||
<h4>{{I18n 'admin.badges.mass_award.upload_csv'}}</h4>
|
||||
<input type='file' id='massAwardCSVUpload' accept='.csv' />
|
||||
</div>
|
||||
{{d-button
|
||||
class="btn-primary"
|
||||
action=(action 'massAward')
|
||||
disabled=saving
|
||||
label="admin.badges.save"}}
|
||||
</form>
|
||||
{{/d-section}}
|
||||
@@ -6,13 +6,18 @@
|
||||
{{d-icon "plus"}}
|
||||
<span>{{i18n 'admin.badges.new'}}</span>
|
||||
{{/link-to}}
|
||||
|
||||
{{#link-to 'adminBadges.award' 'new' class="btn btn-primary"}}
|
||||
{{d-icon "certificate"}}
|
||||
<span>{{i18n 'admin.badges.mass_award.button'}}</span>
|
||||
{{/link-to}}
|
||||
</div>
|
||||
</div>
|
||||
<div class='content-list'>
|
||||
<ul class="admin-badge-list">
|
||||
{{#each model as |badge|}}
|
||||
<li class="admin-badge-list-item">
|
||||
{{#link-to 'adminBadges.show' badge.id}}
|
||||
{{#link-to selectedRoute badge.id}}
|
||||
{{badge-button badge=badge}}
|
||||
{{#if badge.newBadge}}
|
||||
<span class="list-badge">{{i18n 'filters.new.lower_title'}}</span>
|
||||
|
||||
Reference in New Issue
Block a user