DEV: Convert edit-badge-groupings modal to component-based API (#22984)

<img width="603" alt="Screenshot 2023-08-04 at 4 43 02 PM" src="https://github.com/discourse/discourse/assets/50783505/033192cc-2c5c-4be6-adde-9dd89b6b05c3">
<img width="594" alt="Screenshot 2023-08-04 at 4 42 59 PM" src="https://github.com/discourse/discourse/assets/50783505/efe8d8fe-f06c-4940-9c99-66a2f8fb3532">
This commit is contained in:
Isaac Janzen 2023-08-14 12:03:28 -05:00 committed by GitHub
parent d546f34f2f
commit ac4e854a04
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 152 additions and 151 deletions

View File

@ -0,0 +1,55 @@
<DModal
@title={{i18n "admin.badges.badge_groupings.modal_title"}}
@bodyClass="badge-groupings-modal"
@closeModal={{@closeModal}}
>
<:body>
<div class="badge-groupings">
<ul class="badge-groupings-list">
{{#each this.workingCopy as |wc|}}
<li class="badge-grouping-item">
<div class="badge-grouping">
{{#if wc.editing}}
<Input @value={{wc.name}} class="badge-grouping-name-input" />
{{else}}
<span>{{wc.displayName}}</span>
{{/if}}
</div>
<div class="actions">
{{#if wc.editing}}
<DButton @action={{fn (mut wc.editing) false}} @icon="check" />
{{else}}
<DButton
@action={{fn (mut wc.editing) true}}
@disabled={{wc.system}}
@icon="pencil-alt"
/>
{{/if}}
<DButton @action={{fn this.up wc}} @icon="chevron-up" />
<DButton @action={{fn this.down wc}} @icon="chevron-down" />
<DButton
@action={{fn this.delete wc}}
@disabled={{wc.system}}
@icon="times"
/>
</div>
</li>
{{/each}}
</ul>
</div>
<DButton @action={{this.add}} @label="admin.badges.new" />
</:body>
<:footer>
<DButton
@action={{this.saveAll}}
@label="admin.badges.save"
class="btn-primary"
@disabled={{this.submitDisabled}}
/>
<DButton
class="btn-flat d-modal-cancel"
@action={{@closeModal}}
@label="cancel"
/>
</:footer>
</DModal>

View File

@ -0,0 +1,84 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { inject as service } from "@ember/service";
import { action } from "@ember/object";
import I18n from "I18n";
import { A } from "@ember/array";
import { ajax } from "discourse/lib/ajax";
import { TrackedArray } from "@ember-compat/tracked-built-ins";
export default class EditBadgeGroupings extends Component {
@service dialog;
@service store;
@tracked workingCopy = new TrackedArray();
constructor() {
super(...arguments);
let copy = A();
if (this.args.model.badgeGroupings) {
this.args.model.badgeGroupings.forEach((o) =>
copy.pushObject(this.store.createRecord("badge-grouping", o))
);
}
this.workingCopy = copy;
}
@action
up(item) {
this.moveItem(item, -1);
}
@action
down(item) {
this.moveItem(item, 1);
}
@action
delete(item) {
this.workingCopy.removeObject(item);
}
@action
add() {
const obj = this.store.createRecord("badge-grouping", {
editing: true,
name: I18n.t("admin.badges.badge_grouping"),
});
this.workingCopy.pushObject(obj);
}
@action
async saveAll() {
const groupIds = this.workingCopy.map((i) => i.id || -1);
const names = this.workingCopy.map((i) => i.name);
try {
const data = await ajax("/admin/badges/badge_groupings", {
data: { ids: groupIds, names },
type: "POST",
});
this.workingCopy.clear();
data.badge_groupings.forEach((badgeGroup) => {
this.workingCopy.pushObject(
this.store.createRecord("badge-grouping", {
...badgeGroup,
editing: false,
})
);
});
this.args.model.updateGroupings(this.workingCopy);
this.args.closeModal();
} catch {
this.dialog.alert(I18n.t("generic_error"));
}
}
moveItem(item, delta) {
const index = this.workingCopy.indexOf(item);
if (index + delta < 0 || index + delta >= this.workingCopy.length) {
return;
}
this.workingCopy.removeAt(index);
this.workingCopy.insertAt(index + delta, item);
}
}

View File

@ -1,103 +0,0 @@
import { action } from "@ember/object";
import { inject as service } from "@ember/service";
import { A } from "@ember/array";
import Controller from "@ember/controller";
import I18n from "I18n";
import ModalFunctionality from "discourse/mixins/modal-functionality";
import { ajax } from "discourse/lib/ajax";
import { observes } from "discourse-common/utils/decorators";
export default class AdminEditBadgeGroupingsController extends Controller.extend(
ModalFunctionality
) {
@service dialog;
@observes("model")
modelChanged() {
const model = this.model;
const copy = A();
const store = this.store;
if (model) {
model.forEach((o) =>
copy.pushObject(store.createRecord("badge-grouping", o))
);
}
this.set("workingCopy", copy);
}
moveItem(item, delta) {
const copy = this.workingCopy;
const index = copy.indexOf(item);
if (index + delta < 0 || index + delta >= copy.length) {
return;
}
copy.removeAt(index);
copy.insertAt(index + delta, item);
}
@action
up(item) {
this.moveItem(item, -1);
}
@action
down(item) {
this.moveItem(item, 1);
}
@action
delete(item) {
this.workingCopy.removeObject(item);
}
@action
cancel() {
this.setProperties({ model: null, workingCopy: null });
this.send("closeModal");
}
@action
edit(item) {
item.set("editing", true);
}
@action
save(item) {
item.set("editing", false);
}
@action
add() {
const obj = this.store.createRecord("badge-grouping", {
editing: true,
name: I18n.t("admin.badges.badge_grouping"),
});
this.workingCopy.pushObject(obj);
}
@action
saveAll() {
let items = this.workingCopy;
const groupIds = items.map((i) => i.get("id") || -1);
const names = items.map((i) => i.get("name"));
ajax("/admin/badges/badge_groupings", {
data: { ids: groupIds, names },
type: "POST",
}).then(
(data) => {
items = this.model;
items.clear();
data.badge_groupings.forEach((g) => {
items.pushObject(this.store.createRecord("badge-grouping", g));
});
this.setProperties({ model: null, workingCopy: null });
this.send("closeModal");
},
() => this.dialog.alert(I18n.t("generic_error"))
);
}
}

View File

@ -5,9 +5,11 @@ import { ajax } from "discourse/lib/ajax";
import { action, get } from "@ember/object";
import showModal from "discourse/lib/show-modal";
import { inject as service } from "@ember/service";
import EditBadgeGroupingsModal from "../../components/modal/edit-badge-groupings";
export default class AdminBadgesShowRoute extends Route {
@service dialog;
@service modal;
serialize(m) {
return { badge_id: get(m, "id") || "new" };
@ -37,7 +39,17 @@ export default class AdminBadgesShowRoute extends Route {
@action
editGroupings() {
const model = this.controllerFor("admin-badges").get("badgeGroupings");
showModal("admin-edit-badge-groupings", { model, admin: true });
this.modal.show(EditBadgeGroupingsModal, {
model: {
badgeGroupings: model,
updateGroupings: this.updateGroupings,
},
});
}
@action
updateGroupings(groupings) {
this.controllerFor("admin-badges").set("badgeGroupings", groupings);
}
@action

View File

@ -1,46 +0,0 @@
<DModalBody
@title="admin.badges.badge_groupings.modal_title"
@class="badge-groupings-modal"
>
<div class="badge-groupings">
<ul class="badge-groupings-list">
{{#each this.workingCopy as |wc|}}
<li class="badge-grouping-item">
<div class="badge-grouping">
{{#if wc.editing}}
<Input @value={{wc.name}} class="badge-grouping-name-input" />
<DButton @action={{action "save" wc}} @icon="check" />
{{else}}
<span>{{wc.displayName}}</span>
{{/if}}
</div>
<div class="actions">
<DButton
@action={{action "edit" wc}}
@disabled={{wc.system}}
@icon="pencil-alt"
/>
<DButton @action={{action "up" wc}} @icon="chevron-up" />
<DButton @action={{action "down" wc}} @icon="chevron-down" />
<DButton
@action={{action "delete" wc}}
@disabled={{wc.system}}
@icon="times"
/>
</div>
</li>
{{/each}}
</ul>
</div>
<DButton @action={{action "add"}} @label="admin.badges.new" />
</DModalBody>
<div class="modal-footer">
<DButton
@action={{action "saveAll"}}
@label="admin.badges.save"
@class="btn-primary"
@disabled={{this.submitDisabled}}
/>
<DModalCancel @close={{route-action "closeModal"}} />
</div>

View File

@ -47,7 +47,6 @@ const KNOWN_LEGACY_MODALS = [
"user-status",
"admin-penalize-user",
"admin-badge-preview",
"admin-edit-badge-groupings",
"admin-reseed",
"admin-theme-item",
"admin-color-scheme-select-base",