diff --git a/assets/javascripts/discourse/components/assign-user-form.hbs b/assets/javascripts/discourse/components/assign-user-form.hbs
new file mode 100644
index 0000000..2160f23
--- /dev/null
+++ b/assets/javascripts/discourse/components/assign-user-form.hbs
@@ -0,0 +1,57 @@
+
+
{{i18n "discourse_assign.assign_modal.assignee_label"}}
+
+
+ {{#if this.assigneeError}}
+
+ {{d-icon "exclamation-triangle"}}
+ {{i18n "discourse_assign.assign_modal.choose_assignee"}}
+
+ {{/if}}
+
+
+{{#if this.siteSettings.enable_assign_status}}
+
+ {{i18n "discourse_assign.assign_modal.status_label"}}
+
+
+{{/if}}
+
+
+
+ {{i18n "discourse_assign.assign_modal.note_label"}} {{i18n "discourse_assign.assign_modal.optional_label"}}
+
+
+
+
\ No newline at end of file
diff --git a/assets/javascripts/discourse/components/assign-user-form.js b/assets/javascripts/discourse/components/assign-user-form.js
new file mode 100644
index 0000000..ceb8184
--- /dev/null
+++ b/assets/javascripts/discourse/components/assign-user-form.js
@@ -0,0 +1,65 @@
+import Component from "@glimmer/component";
+import { tracked } from "@glimmer/tracking";
+import { action } from "@ember/object";
+import { inject as service } from "@ember/service";
+
+export default class AssignUserForm extends Component {
+ @service taskActions;
+ @service siteSettings;
+ @service capabilities;
+
+ @tracked assigneeError = false;
+ @tracked assigneeName =
+ this.args.model.username || this.args.model.group_name;
+
+ constructor() {
+ super(...arguments);
+
+ this.args.formApi.submit = this.assign;
+ }
+
+ get availableStatuses() {
+ return this.siteSettings.assign_statuses
+ .split("|")
+ .map((status) => ({ id: status, name: status }));
+ }
+
+ get status() {
+ return (
+ this.args.model.status ||
+ this.args.model.target.assignment_status ||
+ this.siteSettings.assign_statuses.split("|")[0]
+ );
+ }
+
+ @action
+ handleTextAreaKeydown(event) {
+ if ((event.ctrlKey || event.metaKey) && event.key === "Enter") {
+ this.assign();
+ }
+ }
+
+ @action
+ async assign() {
+ if (!this.assigneeName) {
+ this.assigneeError = true;
+ return;
+ }
+
+ await this.args.onSubmit();
+ }
+
+ @action
+ assignUsername([name]) {
+ this.assigneeName = name;
+ this.assigneeError = false;
+
+ if (this.taskActions.allowedGroupsForAssignment.includes(name)) {
+ this.args.model.username = null;
+ this.args.model.group_name = name;
+ } else {
+ this.args.model.username = name;
+ this.args.model.group_name = null;
+ }
+ }
+}
diff --git a/assets/javascripts/discourse/components/modal/assign-user.hbs b/assets/javascripts/discourse/components/modal/assign-user.hbs
new file mode 100644
index 0000000..bd73006
--- /dev/null
+++ b/assets/javascripts/discourse/components/modal/assign-user.hbs
@@ -0,0 +1,23 @@
+
+ <:body>
+
+
+
+ <:footer>
+
+
+
+
+
\ No newline at end of file
diff --git a/assets/javascripts/discourse/components/modal/assign-user.js b/assets/javascripts/discourse/components/modal/assign-user.js
new file mode 100644
index 0000000..938a39a
--- /dev/null
+++ b/assets/javascripts/discourse/components/modal/assign-user.js
@@ -0,0 +1,38 @@
+import Component from "@glimmer/component";
+import { action } from "@ember/object";
+import { inject as service } from "@ember/service";
+import I18n from "I18n";
+
+export default class AssignUser extends Component {
+ @service taskActions;
+
+ // `submit` property will be mutated by the `AssignUserForm` component
+ formApi = {
+ submit() {},
+ };
+
+ get title() {
+ let i18nSuffix;
+
+ switch (this.args.model.targetType) {
+ case "Post":
+ i18nSuffix = "_post_modal";
+ break;
+ case "Topic":
+ i18nSuffix = "_modal";
+ break;
+ }
+
+ return I18n.t(
+ `discourse_assign.assign${i18nSuffix}.${
+ this.args.model.reassign ? "reassign_title" : "title"
+ }`
+ );
+ }
+
+ @action
+ async onSubmit() {
+ this.args.closeModal();
+ await this.taskActions.assign(this.args.model);
+ }
+}
diff --git a/assets/javascripts/discourse/connectors/above-review-filters/assigned-to-filter.hbs b/assets/javascripts/discourse/connectors/above-review-filters/assigned-to-filter.hbs
index 7291a22..f219da0 100644
--- a/assets/javascripts/discourse/connectors/above-review-filters/assigned-to-filter.hbs
+++ b/assets/javascripts/discourse/connectors/above-review-filters/assigned-to-filter.hbs
@@ -1,13 +1,12 @@
{{i18n "discourse_assign.assigned_to"}}
{
- if (this.isDestroying || this.isDestroyed) {
- return;
- }
- this.set("assignSuggestions", data.suggestions);
- this.set("allowedGroups", data.assign_allowed_on_groups);
- this.set("allowedGroupsForAssignment", data.assign_allowed_for_groups);
- });
- }
-
- onShow() {
- this.set("assigneeError", false);
- }
-
- onClose() {
- if (this.model.onClose && this.model.username) {
- this.model.onClose(this.model.username);
- }
- }
-
- bulkAction(username, note) {
- return this.topicBulkActions.performAndRefresh({
- type: "assign",
- username,
- note,
- });
- }
-
- @discourseComputed("siteSettings.enable_assign_status")
- statusEnabled() {
- return this.siteSettings.enable_assign_status;
- }
-
- @discourseComputed("siteSettings.assign_statuses")
- availableStatuses() {
- return this.siteSettings.assign_statuses.split("|").map((status) => {
- return { id: status, name: status };
- });
- }
-
- @discourseComputed("siteSettings.assign_statuses", "model.status")
- status() {
- return (
- this.model.status ||
- this.model.target.assignment_status ||
- this.siteSettings.assign_statuses.split("|")[0]
- );
- }
-
- @action
- handleTextAreaKeydown(event) {
- if ((event.ctrlKey || event.metaKey) && event.key === "Enter") {
- this.assign();
- }
- }
-
- @action
- assign() {
- if (this.isBulkAction) {
- return this.bulkAction(this.model.username, this.model.note);
- }
-
- if (!this.assigneeName) {
- this.set("assigneeError", true);
- return;
- }
-
- let path = "/assign/assign";
-
- if (isEmpty(this.model.username)) {
- this.model.target.set("assigned_to_user", null);
- }
-
- if (isEmpty(this.model.group_name)) {
- this.model.target.set("assigned_to_group", null);
- }
-
- if (isEmpty(this.model.username) && isEmpty(this.model.group_name)) {
- path = "/assign/unassign";
- }
-
- this.send("closeModal");
-
- return ajax(path, {
- type: "PUT",
- data: {
- username: this.model.username,
- group_name: this.model.group_name,
- target_id: this.model.target.id,
- target_type: this.model.targetType,
- note: this.model.note,
- status: this.model.status,
- },
- })
- .then(() => {
- this.model.onSuccess?.();
- })
- .catch(popupAjaxError);
- }
-
- @action
- assignUsername([name]) {
- this.set("assigneeError", false);
- this.set("model.allowedGroups", this.taskActions.allowedGroups);
-
- if (this.allowedGroupsForAssignment.includes(name)) {
- this.setProperties({
- "model.username": null,
- "model.group_name": name,
- });
- } else {
- this.setProperties({
- "model.username": name,
- "model.group_name": null,
- });
- }
- }
-}
diff --git a/assets/javascripts/discourse/controllers/bulk-assign.js b/assets/javascripts/discourse/controllers/bulk-assign.js
new file mode 100644
index 0000000..79090b1
--- /dev/null
+++ b/assets/javascripts/discourse/controllers/bulk-assign.js
@@ -0,0 +1,22 @@
+import Controller, { inject as controller } from "@ember/controller";
+import { action } from "@ember/object";
+
+export default class BulkAssign extends Controller {
+ @controller topicBulkActions;
+
+ model;
+
+ // `submit` property will be mutated by the `AssignUserForm` component
+ formApi = {
+ submit() {},
+ };
+
+ @action
+ async assign() {
+ return this.topicBulkActions.performAndRefresh({
+ type: "assign",
+ username: this.model.username,
+ note: this.model.note,
+ });
+ }
+}
diff --git a/assets/javascripts/discourse/controllers/group-assigned-show.js b/assets/javascripts/discourse/controllers/group-assigned-show.js
index ff1a60d..9edba01 100644
--- a/assets/javascripts/discourse/controllers/group-assigned-show.js
+++ b/assets/javascripts/discourse/controllers/group-assigned-show.js
@@ -43,17 +43,16 @@ export default class GroupAssignedShow extends UserTopicsList {
}
@action
- unassign(targetId, targetType = "Topic") {
- this.taskActions
- .unassign(targetId, targetType)
- .then(() => this.send("changeAssigned"));
+ async unassign(targetId, targetType = "Topic") {
+ await this.taskActions.unassign(targetId, targetType);
+ this.send("changeAssigned");
}
@action
reassign(topic) {
- this.taskActions
- .assign(topic)
- .set("model.onSuccess", () => this.send("changeAssigned"));
+ this.taskActions.showAssignModal(topic, {
+ onSuccess: () => this.send("changeAssigned"),
+ });
}
@action
diff --git a/assets/javascripts/discourse/controllers/user-activity-assigned.js b/assets/javascripts/discourse/controllers/user-activity-assigned.js
index 4ebccae..06d0e3b 100644
--- a/assets/javascripts/discourse/controllers/user-activity-assigned.js
+++ b/assets/javascripts/discourse/controllers/user-activity-assigned.js
@@ -57,17 +57,16 @@ export default class UserActivityAssigned extends UserTopicsList {
}
@action
- unassign(targetId, targetType = "Topic") {
- this.taskActions
- .unassign(targetId, targetType)
- .then(() => this.send("changeAssigned"));
+ async unassign(targetId, targetType = "Topic") {
+ await this.taskActions.unassign(targetId, targetType);
+ this.send("changeAssigned");
}
@action
reassign(topic) {
- this.taskActions
- .assign(topic)
- .set("model.onSuccess", () => this.send("changeAssigned"));
+ this.taskActions.showAssignModal(topic, {
+ onSuccess: () => this.send("changeAssigned"),
+ });
}
@action
diff --git a/assets/javascripts/discourse/initializers/extend-for-assigns.js b/assets/javascripts/discourse/initializers/extend-for-assigns.js
index 62955f7..0b1c469 100644
--- a/assets/javascripts/discourse/initializers/extend-for-assigns.js
+++ b/assets/javascripts/discourse/initializers/extend-for-assigns.js
@@ -11,7 +11,6 @@ import SearchAdvancedOptions from "discourse/components/search-advanced-options"
import TopicButtonAction, {
addBulkButton,
} from "discourse/controllers/topic-bulk-actions";
-import { inject as controller } from "@ember/controller";
import I18n from "I18n";
import { isEmpty } from "@ember/utils";
import { registerTopicFooterDropdown } from "discourse/lib/register-topic-footer-dropdown";
@@ -50,7 +49,7 @@ function registerTopicFooterButtons(api) {
registerTopicFooterDropdown({
id: "reassign",
- action(id) {
+ async action(id) {
if (!this.currentUser?.can_assign) {
return;
}
@@ -61,36 +60,34 @@ function registerTopicFooterButtons(api) {
case "unassign": {
this.set("topic.assigned_to_user", null);
this.set("topic.assigned_to_group", null);
- taskActions.unassign(this.topic.id).then(() => {
- this.appEvents.trigger("post-stream:refresh", {
- id: this.topic.postStream.firstPostId,
- });
+
+ await taskActions.unassign(this.topic.id);
+
+ this.appEvents.trigger("post-stream:refresh", {
+ id: this.topic.postStream.firstPostId,
});
break;
}
case "reassign-self": {
this.set("topic.assigned_to_user", null);
this.set("topic.assigned_to_group", null);
- taskActions
- .reassignUserToTopic(this.currentUser, this.topic)
- .then(() => {
- this.appEvents.trigger("post-stream:refresh", {
- id: this.topic.postStream.firstPostId,
- });
- });
+
+ await taskActions.reassignUserToTopic(this.currentUser, this.topic);
+
+ this.appEvents.trigger("post-stream:refresh", {
+ id: this.topic.postStream.firstPostId,
+ });
break;
}
case "reassign": {
- taskActions
- .assign(this.topic, {
- targetType: "Topic",
- isAssigned: this.topic.isAssigned(),
- })
- .set("model.onSuccess", () => {
+ await taskActions.showAssignModal(this.topic, {
+ targetType: "Topic",
+ isAssigned: this.topic.isAssigned(),
+ onSuccess: () =>
this.appEvents.trigger("post-stream:refresh", {
id: this.topic.postStream.firstPostId,
- });
- });
+ }),
+ });
break;
}
}
@@ -195,7 +192,7 @@ function registerTopicFooterButtons(api) {
translatedLabel() {
return I18n.t("discourse_assign.assign.title");
},
- action() {
+ async action() {
if (!this.currentUser?.can_assign) {
return;
}
@@ -205,16 +202,18 @@ function registerTopicFooterButtons(api) {
if (this.topic.isAssigned()) {
this.set("topic.assigned_to_user", null);
this.set("topic.assigned_to_group", null);
- taskActions.unassign(this.topic.id, "Topic").then(() => {
- this.appEvents.trigger("post-stream:refresh", {
- id: this.topic.postStream.firstPostId,
- });
+
+ await taskActions.unassign(this.topic.id, "Topic");
+
+ this.appEvents.trigger("post-stream:refresh", {
+ id: this.topic.postStream.firstPostId,
});
} else {
- taskActions.assign(this.topic).set("model.onSuccess", () => {
- this.appEvents.trigger("post-stream:refresh", {
- id: this.topic.postStream.firstPostId,
- });
+ await taskActions.showAssignModal(this.topic, {
+ onSuccess: () =>
+ this.appEvents.trigger("post-stream:refresh", {
+ id: this.topic.postStream.firstPostId,
+ }),
});
}
},
@@ -333,7 +332,7 @@ function registerTopicFooterButtons(api) {
`${label} `
);
},
- action() {
+ async action() {
if (!this.currentUser?.can_assign) {
return;
}
@@ -342,10 +341,11 @@ function registerTopicFooterButtons(api) {
this.set("topic.assigned_to_user", null);
this.set("topic.assigned_to_group", null);
- taskActions.reassignUserToTopic(this.currentUser, this.topic).then(() => {
- this.appEvents.trigger("post-stream:refresh", {
- id: this.topic.postStream.firstPostId,
- });
+
+ await taskActions.reassignUserToTopic(this.currentUser, this.topic);
+
+ this.appEvents.trigger("post-stream:refresh", {
+ id: this.topic.postStream.firstPostId,
});
},
dropdown() {
@@ -382,23 +382,21 @@ function registerTopicFooterButtons(api) {
`${label} `
);
},
- action() {
+ async action() {
if (!this.currentUser?.can_assign) {
return;
}
const taskActions = getOwner(this).lookup("service:task-actions");
- taskActions
- .assign(this.topic, {
- targetType: "Topic",
- isAssigned: this.topic.isAssigned(),
- })
- .set("model.onSuccess", () => {
+ await taskActions.showAssignModal(this.topic, {
+ targetType: "Topic",
+ isAssigned: this.topic.isAssigned(),
+ onSuccess: () =>
this.appEvents.trigger("post-stream:refresh", {
id: this.topic.postStream.firstPostId,
- });
- });
+ }),
+ });
},
dropdown() {
return this.currentUser?.can_assign && this.topic.isAssigned();
@@ -439,7 +437,7 @@ function initialize(api) {
},
before: "top",
});
- if (api.getCurrentUser() && api.getCurrentUser().can_assign) {
+ if (api.getCurrentUser()?.can_assign) {
api.addPostMenuButton("assign", (post) => {
if (post.firstPost) {
return;
@@ -465,13 +463,15 @@ function initialize(api) {
};
}
});
+
api.attachWidgetAction("post", "assignPost", function () {
const taskActions = getOwner(this).lookup("service:task-actions");
- taskActions.assign(this.model, {
+ taskActions.showAssignModal(this.model, {
isAssigned: false,
targetType: "Post",
});
});
+
api.attachWidgetAction("post", "unassignPost", function () {
const taskActions = getOwner(this).lookup("service:task-actions");
taskActions.unassign(this.model.id, "Post").then(() => {
@@ -488,7 +488,7 @@ function initialize(api) {
});
api.addAdvancedSearchOptions(
- api.getCurrentUser() && api.getCurrentUser().can_assign
+ api.getCurrentUser()?.can_assign
? {
inOptionsForUsers: [
{
@@ -914,25 +914,27 @@ export default {
});
TopicButtonAction.reopen({
- assignUser: controller("assign-user"),
actions: {
showReAssign() {
- this.set("assignUser.isBulkAction", true);
- this.set("assignUser.model", { username: "", note: "" });
- this.send("changeBulkTemplate", "modal/assign-user");
+ const controller = getOwner(this).lookup("controller:bulk-assign");
+ controller.set("model", { username: "", note: "" });
+ this.send("changeBulkTemplate", "modal/bulk-assign");
},
+
unassignTopics() {
this.performAndRefresh({ type: "unassign" });
},
},
});
+
addBulkButton("showReAssign", "assign", {
icon: "user-plus",
- class: "btn-default",
+ class: "btn-default assign-topics",
});
+
addBulkButton("unassignTopics", "unassign", {
icon: "user-times",
- class: "btn-default",
+ class: "btn-default unassign-topics",
});
}
diff --git a/assets/javascripts/discourse/services/task-actions.js b/assets/javascripts/discourse/services/task-actions.js
index 6bfa01d..aaf09fc 100644
--- a/assets/javascripts/discourse/services/task-actions.js
+++ b/assets/javascripts/discourse/services/task-actions.js
@@ -1,15 +1,38 @@
-import Service from "@ember/service";
+import Service, { inject as service } from "@ember/service";
import { ajax } from "discourse/lib/ajax";
-import showModal from "discourse/lib/show-modal";
+import AssignUser from "../components/modal/assign-user";
+import { tracked } from "@glimmer/tracking";
+import { isEmpty } from "@ember/utils";
+import { popupAjaxError } from "discourse/lib/ajax-error";
export default class TaskActions extends Service {
- i18nSuffix(targetType) {
- switch (targetType) {
- case "Post":
- return "_post_modal";
- case "Topic":
- return "_modal";
+ @service modal;
+
+ @tracked allowedGroups;
+ @tracked allowedGroupsForAssignment;
+ #suggestionsPromise;
+ @tracked _suggestions;
+
+ get suggestions() {
+ if (this._suggestions) {
+ return this._suggestions;
}
+
+ this.#suggestionsPromise ||= this.#fetchSuggestions();
+
+ return null;
+ }
+
+ async #fetchSuggestions() {
+ const data = await ajax("/assign/suggestions");
+
+ if (this.isDestroying || this.isDestroyed) {
+ return;
+ }
+
+ this._suggestions = data.suggestions;
+ this.allowedGroups = data.assign_allowed_on_groups;
+ this.allowedGroupsForAssignment = data.assign_allowed_for_groups;
}
unassign(targetId, targetType = "Topic") {
@@ -22,19 +45,19 @@ export default class TaskActions extends Service {
});
}
- assign(target, options = { isAssigned: false, targetType: "Topic" }) {
- return showModal("assign-user", {
- title:
- "discourse_assign.assign" +
- this.i18nSuffix(options.targetType) +
- `.${options.isAssigned ? "reassign_title" : "title"}`,
+ showAssignModal(
+ target,
+ { isAssigned = false, targetType = "Topic", onSuccess }
+ ) {
+ return this.modal.show(AssignUser, {
model: {
- reassign: options.isAssigned,
+ reassign: isAssigned,
username: target.assigned_to_user?.username,
group_name: target.assigned_to_group?.name,
- target,
- targetType: options.targetType,
status: target.assignment_status,
+ target,
+ targetType,
+ onSuccess,
},
});
}
@@ -50,4 +73,37 @@ export default class TaskActions extends Service {
},
});
}
+
+ async assign(model) {
+ if (isEmpty(model.username)) {
+ model.target.set("assigned_to_user", null);
+ }
+
+ if (isEmpty(model.group_name)) {
+ model.target.set("assigned_to_group", null);
+ }
+
+ let path = "/assign/assign";
+ if (isEmpty(model.username) && isEmpty(model.group_name)) {
+ path = "/assign/unassign";
+ }
+
+ try {
+ await ajax(path, {
+ type: "PUT",
+ data: {
+ username: model.username,
+ group_name: model.group_name,
+ target_id: model.target.id,
+ target_type: model.targetType,
+ note: model.note,
+ status: model.status,
+ },
+ });
+
+ model.onSuccess?.();
+ } catch (error) {
+ popupAjaxError(error);
+ }
+ }
}
diff --git a/assets/javascripts/discourse/templates/modal/assign-user.hbs b/assets/javascripts/discourse/templates/modal/assign-user.hbs
deleted file mode 100644
index 4fba99c..0000000
--- a/assets/javascripts/discourse/templates/modal/assign-user.hbs
+++ /dev/null
@@ -1,76 +0,0 @@
-
-
-
-
{{i18n "discourse_assign.assign_modal.assignee_label"}}
-
- {{#if this.assigneeError}}
-
- {{d-icon "exclamation-triangle"}}
- {{i18n "discourse_assign.assign_modal.choose_assignee"}}
-
- {{/if}}
-
-
- {{#if this.statusEnabled}}
-
- {{i18n "discourse_assign.assign_modal.status_label"}}
-
-
- {{/if}}
-
-
-
- {{i18n "discourse_assign.assign_modal.note_label"}} {{i18n "discourse_assign.assign_modal.optional_label"}}
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/assets/javascripts/discourse/templates/modal/bulk-assign.hbs b/assets/javascripts/discourse/templates/modal/bulk-assign.hbs
new file mode 100644
index 0000000..deb06aa
--- /dev/null
+++ b/assets/javascripts/discourse/templates/modal/bulk-assign.hbs
@@ -0,0 +1,19 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/assets/stylesheets/assigns.scss b/assets/stylesheets/assigns.scss
index 79f72dd..dc4ece3 100644
--- a/assets/stylesheets/assigns.scss
+++ b/assets/stylesheets/assigns.scss
@@ -66,12 +66,12 @@
margin-left: 5px;
}
-.assign-user-modal {
+.modal.assign {
.modal-inner-container {
width: 400px;
}
- .assign.modal-body {
+ .modal-body {
overflow-y: unset;
}
diff --git a/test/javascripts/acceptance/assign-enabled-test.js b/test/javascripts/acceptance/assign-enabled-test.js
index 4a6785c..13ed78a 100644
--- a/test/javascripts/acceptance/assign-enabled-test.js
+++ b/test/javascripts/acceptance/assign-enabled-test.js
@@ -5,8 +5,12 @@ import {
acceptance,
updateCurrentUser,
} from "discourse/tests/helpers/qunit-helpers";
-import { click, visit } from "@ember/test-helpers";
+import { click, fillIn, visit } from "@ember/test-helpers";
import { test } from "qunit";
+import pretender, {
+ parsePostData,
+ response,
+} from "discourse/tests/helpers/create-pretender";
acceptance("Discourse Assign | Assign mobile", function (needs) {
needs.user();
@@ -30,10 +34,6 @@ acceptance("Discourse Assign | Assign mobile", function (needs) {
],
});
});
-
- server.put("/assign/assign", () => {
- return helper.response({ success: true });
- });
});
test("Footer dropdown contains button", async function (assert) {
@@ -44,7 +44,7 @@ acceptance("Discourse Assign | Assign mobile", function (needs) {
assert.true(menu.rowByValue("assign").exists());
await menu.selectRowByValue("assign");
- assert.dom(".assign.modal-body").exists("assign modal opens");
+ assert.dom(".assign.modal").exists("assign modal opens");
});
});
@@ -71,32 +71,53 @@ acceptance("Discourse Assign | Assign desktop", function (needs) {
],
});
});
-
- server.put("/assign/assign", () => {
- return helper.response({ success: true });
- });
});
- test("Post contains hidden assign button", async function (assert) {
+ test("Assigning user to a post", async function (assert) {
await visit("/t/internationalization-localization/280");
assert
.dom("#post_2 .extra-buttons .d-icon-user-plus")
.doesNotExist("assign to post button is hidden");
- await click("#post_2 button.show-more-actions");
+ await click("#post_2 button.show-more-actions");
assert
.dom("#post_2 .extra-buttons .d-icon-user-plus")
.exists("assign to post button exists");
+
await click("#post_2 .extra-buttons .d-icon-user-plus");
- assert.dom(".assign.modal-body").exists("assign modal opens");
+ assert.dom(".assign.modal").exists("assign modal opens");
+
+ const menu = selectKit(".assign.modal .user-chooser");
+ assert.true(menu.isExpanded(), "user selector is expanded");
+
+ await click(".assign.modal .btn-primary");
+ assert.dom(".error-label").includesText("Choose a user to assign");
+
+ await menu.expand();
+ await menu.selectRowByIndex(0);
+ assert.strictEqual(menu.header().value(), "eviltrout");
+ assert.dom(".error-label").doesNotExist();
+
+ pretender.put("/assign/assign", ({ requestBody }) => {
+ const body = parsePostData(requestBody);
+ assert.strictEqual(body.target_type, "Post");
+ assert.strictEqual(body.username, "eviltrout");
+ assert.strictEqual(body.note, "a note!");
+ return response({ success: true });
+ });
+
+ await fillIn("#assign-modal-note", "a note!");
+ await click(".assign.modal .btn-primary");
+
+ assert.dom(".assign.modal").doesNotExist("assign modal closes");
});
test("Footer dropdown contains button", async function (assert) {
await visit("/t/internationalization-localization/280");
await click("#topic-footer-button-assign");
- assert.dom(".assign.modal-body").exists("assign modal opens");
+ assert.dom(".assign.modal").exists("assign modal opens");
});
});
@@ -127,10 +148,6 @@ acceptance("Discourse Assign | Assign Status enabled", function (needs) {
],
});
});
-
- server.put("/assign/assign", () => {
- return helper.response({ success: true });
- });
});
test("Modal contains status dropdown", async function (assert) {
@@ -138,7 +155,7 @@ acceptance("Discourse Assign | Assign Status enabled", function (needs) {
await click("#topic-footer-button-assign");
assert
- .dom(".assign.modal-body #assign-status")
+ .dom(".assign.modal #assign-status")
.exists("assign status dropdown exists");
});
});
@@ -166,10 +183,6 @@ acceptance("Discourse Assign | Assign Status disabled", function (needs) {
],
});
});
-
- server.put("/assign/assign", () => {
- return helper.response({ success: true });
- });
});
test("Modal contains status dropdown", async function (assert) {
@@ -177,7 +190,7 @@ acceptance("Discourse Assign | Assign Status disabled", function (needs) {
await click("#topic-footer-button-assign");
assert
- .dom(".assign.modal-body #assign-status")
+ .dom(".assign.modal #assign-status")
.doesNotExist("assign status dropdown doesn't exists");
});
});
diff --git a/test/javascripts/acceptance/bulk-actions-test.js b/test/javascripts/acceptance/bulk-actions-test.js
new file mode 100644
index 0000000..5745339
--- /dev/null
+++ b/test/javascripts/acceptance/bulk-actions-test.js
@@ -0,0 +1,82 @@
+import { acceptance, query } from "discourse/tests/helpers/qunit-helpers";
+import { test } from "qunit";
+import { click, fillIn, visit } from "@ember/test-helpers";
+import selectKit from "discourse/tests/helpers/select-kit-helper";
+import pretender, {
+ parsePostData,
+ response,
+} from "discourse/tests/helpers/create-pretender";
+import I18n from "I18n";
+
+acceptance("Discourse Assign | Bulk actions", function (needs) {
+ needs.user({
+ moderator: true,
+ can_assign: true,
+ });
+ needs.settings({ assign_enabled: true });
+
+ needs.pretender((server, helper) => {
+ server.get("/assign/suggestions", () => {
+ return helper.response({
+ success: true,
+ assign_allowed_groups: false,
+ assign_allowed_for_groups: [],
+ suggestions: [
+ {
+ id: 19,
+ username: "eviltrout",
+ name: "Robin Ward",
+ avatar_template:
+ "/user_avatar/meta.discourse.org/eviltrout/{size}/5275_2.png",
+ },
+ ],
+ });
+ });
+ });
+
+ test("Assigning users to topics", async function (assert) {
+ await visit("/latest");
+ await click("button.bulk-select");
+
+ const topic1 = query(".topic-list-body tr:nth-child(1)");
+ const topic2 = query(".topic-list-body tr:nth-child(2)");
+ await click(topic1.querySelector("input.bulk-select"));
+ await click(topic2.querySelector("input.bulk-select"));
+
+ await click(".bulk-select-actions");
+
+ assert
+ .dom("#discourse-modal-title")
+ .includesText(I18n.t("topics.bulk.actions"), "opens bulk-select modal");
+
+ await click("button.assign-topics");
+
+ const menu = selectKit(".topic-bulk-actions-modal .user-chooser");
+ assert.true(menu.isExpanded(), "user selector is expanded");
+
+ await click(".topic-bulk-actions-modal .btn-primary");
+ assert.dom(".error-label").includesText("Choose a user to assign");
+
+ await menu.expand();
+ await menu.selectRowByIndex(0);
+ assert.strictEqual(menu.header().value(), "eviltrout");
+
+ pretender.put("/topics/bulk", ({ requestBody }) => {
+ const body = parsePostData(requestBody);
+ assert.deepEqual(body.operation, {
+ type: "assign",
+ username: "eviltrout",
+ note: "a note!",
+ });
+ assert.deepEqual(body["topic_ids[]"], [
+ topic1.dataset.topicId,
+ topic2.dataset.topicId,
+ ]);
+
+ return response({ success: true });
+ });
+
+ await fillIn("#assign-modal-note", "a note!");
+ await click(".topic-bulk-actions-modal .btn-primary");
+ });
+});
diff --git a/test/javascripts/integrations/components/group-assigned-filter-test.js b/test/javascripts/integrations/components/group-assigned-filter-test.js
new file mode 100644
index 0000000..325cd19
--- /dev/null
+++ b/test/javascripts/integrations/components/group-assigned-filter-test.js
@@ -0,0 +1,35 @@
+import componentTest, {
+ setupRenderingTest,
+} from "discourse/tests/helpers/component-test";
+import { discourseModule, query } from "discourse/tests/helpers/qunit-helpers";
+import hbs from "htmlbars-inline-precompile";
+
+discourseModule(
+ "Discourse Assign | Integration | Component | group-assigned-filter",
+ function (hooks) {
+ setupRenderingTest(hooks);
+
+ componentTest("displays username and name", {
+ template: hbs` `,
+
+ beforeEach() {
+ this.set("filter", {
+ id: 2,
+ username: "Ahmed",
+ name: "Ahmed Gagan",
+ avatar_template: "/letter_avatar_proxy/v4/letter/a/8c91f0/{size}.png",
+ title: "trust_level_0",
+ last_posted_at: "2020-06-22T10:15:54.532Z",
+ last_seen_at: "2020-07-07T11:55:59.437Z",
+ added_at: "2020-06-22T09:55:31.692Z",
+ timezone: "Asia/Calcutta",
+ });
+ },
+
+ test(assert) {
+ assert.strictEqual(query(".assign-username").innerText, "Ahmed");
+ assert.strictEqual(query(".assign-name").innerText, "Ahmed Gagan");
+ },
+ });
+ }
+);
diff --git a/test/javascripts/unit/controllers/assign-user-test.js b/test/javascripts/unit/controllers/assign-user-test.js
deleted file mode 100644
index cb735d2..0000000
--- a/test/javascripts/unit/controllers/assign-user-test.js
+++ /dev/null
@@ -1,36 +0,0 @@
-import { module, test } from "qunit";
-import { setupRenderingTest } from "discourse/tests/helpers/component-test";
-import EmberObject from "@ember/object";
-import pretender, { response } from "discourse/tests/helpers/create-pretender";
-import { getOwner } from "discourse-common/lib/get-owner";
-
-module("Discourse Assign | Unit | Controller | assign-user", function (hooks) {
- setupRenderingTest(hooks);
-
- test("assigning a user by selector does not close the modal", async function (assert) {
- pretender.get("/assign/suggestions", () =>
- response({
- suggestions: [],
- assign_allowed_on_groups: ["nat"],
- assign_allowed_for_groups: [],
- })
- );
-
- let modalClosed = false;
- const controller = getOwner(this).lookup("controller:assign-user");
- controller.setProperties({
- model: {
- target: EmberObject.create({}),
- },
- allowedGroupsForAssignment: ["nat"],
- taskActions: { allowedGroups: [] },
- });
- controller.set("actions.closeModal", () => {
- modalClosed = true;
- });
-
- await controller.assignUsername("nat");
-
- assert.false(modalClosed);
- });
-});
diff --git a/test/javascripts/unit/services/task-actions-test.js b/test/javascripts/unit/services/task-actions-test.js
deleted file mode 100644
index 723fcb8..0000000
--- a/test/javascripts/unit/services/task-actions-test.js
+++ /dev/null
@@ -1,54 +0,0 @@
-import { module, test } from "qunit";
-import { setupTest } from "ember-qunit";
-import sinon from "sinon";
-import * as showModal from "discourse/lib/show-modal";
-import pretender, { response } from "discourse/tests/helpers/create-pretender";
-import { getOwner } from "discourse-common/lib/get-owner";
-
-module("Discourse Assign | Unit | Service | task-actions", function (hooks) {
- setupTest(hooks);
-
- test("assign", function (assert) {
- const stub = sinon.stub(showModal, "default").returns("the modal");
- const service = getOwner(this).lookup("service:task-actions");
- const target = {
- assigned_to_user: { username: "tomtom" },
- assigned_to_group: { name: "cats" },
- };
-
- const modal = service.assign(target);
- const modalCall = stub.getCall(0).args;
-
- assert.strictEqual(modal, "the modal");
- assert.strictEqual(modalCall[0], "assign-user");
- assert.deepEqual(modalCall[1], {
- title: "discourse_assign.assign_modal.title",
- model: {
- reassign: false,
- username: "tomtom",
- group_name: "cats",
- target,
- targetType: "Topic",
- status: undefined,
- },
- });
- });
-
- test("reassignUserToTopic", async function (assert) {
- const service = getOwner(this).lookup("service:task-actions");
- const target = { id: 1 };
- const user = { username: "tomtom" };
- let assignRequest;
- pretender.put("/assign/assign", (request) => {
- assignRequest = request;
- return response({});
- });
-
- await service.reassignUserToTopic(user, target);
-
- assert.strictEqual(
- assignRequest.requestBody,
- "username=tomtom&target_id=1&target_type=Topic"
- );
- });
-});