DEV: Use the new component-based modal API (#489)
Co-authored-by: David Taylor <david@taylorhq.com>
This commit is contained in:
parent
a4ea754c1d
commit
1c079b792b
|
@ -0,0 +1,57 @@
|
||||||
|
<div class="control-group {{if this.assigneeError 'assignee-error'}}">
|
||||||
|
<label>{{i18n "discourse_assign.assign_modal.assignee_label"}}</label>
|
||||||
|
<AssigneeChooser
|
||||||
|
autocomplete="off"
|
||||||
|
@value={{this.assigneeName}}
|
||||||
|
@onChange={{this.assignUsername}}
|
||||||
|
@showUserStatus={{true}}
|
||||||
|
@options={{hash
|
||||||
|
mobilePlacementStrategy="absolute"
|
||||||
|
includeGroups=true
|
||||||
|
customSearchOptions=(hash
|
||||||
|
assignableGroups=true defaultSearchResults=this.taskActions.suggestions
|
||||||
|
)
|
||||||
|
groupMembersOf=this.taskActions.allowedGroups
|
||||||
|
maximum=1
|
||||||
|
autofocus=(not this.capabilities.touch)
|
||||||
|
tabindex=1
|
||||||
|
expandedOnInsert=(not this.assigneeName)
|
||||||
|
caretUpIcon="search"
|
||||||
|
caretDownIcon="search"
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{{#if this.assigneeError}}
|
||||||
|
<span class="error-label">
|
||||||
|
{{d-icon "exclamation-triangle"}}
|
||||||
|
{{i18n "discourse_assign.assign_modal.choose_assignee"}}
|
||||||
|
</span>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#if this.siteSettings.enable_assign_status}}
|
||||||
|
<div class="control-group assign-status">
|
||||||
|
<label>{{i18n "discourse_assign.assign_modal.status_label"}}</label>
|
||||||
|
<ComboBox
|
||||||
|
@id="assign-status"
|
||||||
|
@content={{this.availableStatuses}}
|
||||||
|
@value={{this.status}}
|
||||||
|
@onChange={{action (mut @model.status)}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<div class="control-group assign-status">
|
||||||
|
<label>
|
||||||
|
{{i18n "discourse_assign.assign_modal.note_label"}} <span
|
||||||
|
class="label-optional"
|
||||||
|
>{{i18n "discourse_assign.assign_modal.optional_label"}}</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<Textarea
|
||||||
|
id="assign-modal-note"
|
||||||
|
@value={{@model.note}}
|
||||||
|
{{! template-lint-disable no-down-event-binding }}
|
||||||
|
{{on "keydown" this.handleTextAreaKeydown}}
|
||||||
|
/>
|
||||||
|
</div>
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
<DModal class="assign" @title={{this.title}} @closeModal={{@closeModal}}>
|
||||||
|
<:body>
|
||||||
|
<AssignUserForm
|
||||||
|
@model={{@model}}
|
||||||
|
@onSubmit={{this.onSubmit}}
|
||||||
|
@formApi={{this.formApi}}
|
||||||
|
/>
|
||||||
|
</:body>
|
||||||
|
|
||||||
|
<:footer>
|
||||||
|
<DButton
|
||||||
|
class="btn-primary"
|
||||||
|
@action={{this.formApi.submit}}
|
||||||
|
@label={{if
|
||||||
|
@model.reassign
|
||||||
|
"discourse_assign.reassign.title"
|
||||||
|
"discourse_assign.assign_modal.assign"
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<DModalCancel @close={{@closeModal}} />
|
||||||
|
</:footer>
|
||||||
|
</DModal>
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,13 +1,12 @@
|
||||||
<div class="reviewable-filter discourse-assign-assign-to-filter">
|
<div class="reviewable-filter discourse-assign-assign-to-filter">
|
||||||
<label class="filter-label">{{i18n "discourse_assign.assigned_to"}}</label>
|
<label class="filter-label">{{i18n "discourse_assign.assigned_to"}}</label>
|
||||||
<EmailGroupUserChooser
|
<EmailGroupUserChooser
|
||||||
@value={{this.additionalFilters.assigned_to}}
|
|
||||||
@onChange={{action "updateAssignedTo"}}
|
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
|
@value={{this.additionalFilters.assigned_to}}
|
||||||
|
@onChange={{this.updateAssignedTo}}
|
||||||
@options={{hash
|
@options={{hash
|
||||||
maximum=1
|
maximum=1
|
||||||
fullWidthWrap=true
|
fullWidthWrap=true
|
||||||
filterPlaceholder=this.placeholderKey
|
|
||||||
includeGroups=false
|
includeGroups=false
|
||||||
groupMembersOf=this.allowedGroups
|
groupMembersOf=this.allowedGroups
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -1,145 +0,0 @@
|
||||||
import Controller, { inject as controller } from "@ember/controller";
|
|
||||||
import ModalFunctionality from "discourse/mixins/modal-functionality";
|
|
||||||
import { action } from "@ember/object";
|
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
|
||||||
import { not, or } from "@ember/object/computed";
|
|
||||||
import { inject as service } from "@ember/service";
|
|
||||||
import { isEmpty } from "@ember/utils";
|
|
||||||
import { ajax } from "discourse/lib/ajax";
|
|
||||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
|
||||||
|
|
||||||
export default class AssignUser extends Controller.extend(ModalFunctionality) {
|
|
||||||
@service taskActions;
|
|
||||||
@controller topicBulkActions;
|
|
||||||
|
|
||||||
assignSuggestions = null;
|
|
||||||
allowedGroups = null;
|
|
||||||
assigneeError = false;
|
|
||||||
|
|
||||||
@not("capabilities.touch") autofocus;
|
|
||||||
@or("model.username", "model.group_name") assigneeName;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super(...arguments);
|
|
||||||
|
|
||||||
this.set("allowedGroups", []);
|
|
||||||
this.set("assigneeError", false);
|
|
||||||
|
|
||||||
ajax("/assign/suggestions").then((data) => {
|
|
||||||
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,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -43,17 +43,16 @@ export default class GroupAssignedShow extends UserTopicsList {
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
unassign(targetId, targetType = "Topic") {
|
async unassign(targetId, targetType = "Topic") {
|
||||||
this.taskActions
|
await this.taskActions.unassign(targetId, targetType);
|
||||||
.unassign(targetId, targetType)
|
this.send("changeAssigned");
|
||||||
.then(() => this.send("changeAssigned"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
reassign(topic) {
|
reassign(topic) {
|
||||||
this.taskActions
|
this.taskActions.showAssignModal(topic, {
|
||||||
.assign(topic)
|
onSuccess: () => this.send("changeAssigned"),
|
||||||
.set("model.onSuccess", () => this.send("changeAssigned"));
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
|
|
|
@ -57,17 +57,16 @@ export default class UserActivityAssigned extends UserTopicsList {
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
unassign(targetId, targetType = "Topic") {
|
async unassign(targetId, targetType = "Topic") {
|
||||||
this.taskActions
|
await this.taskActions.unassign(targetId, targetType);
|
||||||
.unassign(targetId, targetType)
|
this.send("changeAssigned");
|
||||||
.then(() => this.send("changeAssigned"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
reassign(topic) {
|
reassign(topic) {
|
||||||
this.taskActions
|
this.taskActions.showAssignModal(topic, {
|
||||||
.assign(topic)
|
onSuccess: () => this.send("changeAssigned"),
|
||||||
.set("model.onSuccess", () => this.send("changeAssigned"));
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
|
|
|
@ -11,7 +11,6 @@ import SearchAdvancedOptions from "discourse/components/search-advanced-options"
|
||||||
import TopicButtonAction, {
|
import TopicButtonAction, {
|
||||||
addBulkButton,
|
addBulkButton,
|
||||||
} from "discourse/controllers/topic-bulk-actions";
|
} from "discourse/controllers/topic-bulk-actions";
|
||||||
import { inject as controller } from "@ember/controller";
|
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
import { isEmpty } from "@ember/utils";
|
import { isEmpty } from "@ember/utils";
|
||||||
import { registerTopicFooterDropdown } from "discourse/lib/register-topic-footer-dropdown";
|
import { registerTopicFooterDropdown } from "discourse/lib/register-topic-footer-dropdown";
|
||||||
|
@ -50,7 +49,7 @@ function registerTopicFooterButtons(api) {
|
||||||
registerTopicFooterDropdown({
|
registerTopicFooterDropdown({
|
||||||
id: "reassign",
|
id: "reassign",
|
||||||
|
|
||||||
action(id) {
|
async action(id) {
|
||||||
if (!this.currentUser?.can_assign) {
|
if (!this.currentUser?.can_assign) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -61,36 +60,34 @@ function registerTopicFooterButtons(api) {
|
||||||
case "unassign": {
|
case "unassign": {
|
||||||
this.set("topic.assigned_to_user", null);
|
this.set("topic.assigned_to_user", null);
|
||||||
this.set("topic.assigned_to_group", null);
|
this.set("topic.assigned_to_group", null);
|
||||||
taskActions.unassign(this.topic.id).then(() => {
|
|
||||||
this.appEvents.trigger("post-stream:refresh", {
|
await taskActions.unassign(this.topic.id);
|
||||||
id: this.topic.postStream.firstPostId,
|
|
||||||
});
|
this.appEvents.trigger("post-stream:refresh", {
|
||||||
|
id: this.topic.postStream.firstPostId,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "reassign-self": {
|
case "reassign-self": {
|
||||||
this.set("topic.assigned_to_user", null);
|
this.set("topic.assigned_to_user", null);
|
||||||
this.set("topic.assigned_to_group", null);
|
this.set("topic.assigned_to_group", null);
|
||||||
taskActions
|
|
||||||
.reassignUserToTopic(this.currentUser, this.topic)
|
await taskActions.reassignUserToTopic(this.currentUser, this.topic);
|
||||||
.then(() => {
|
|
||||||
this.appEvents.trigger("post-stream:refresh", {
|
this.appEvents.trigger("post-stream:refresh", {
|
||||||
id: this.topic.postStream.firstPostId,
|
id: this.topic.postStream.firstPostId,
|
||||||
});
|
});
|
||||||
});
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "reassign": {
|
case "reassign": {
|
||||||
taskActions
|
await taskActions.showAssignModal(this.topic, {
|
||||||
.assign(this.topic, {
|
targetType: "Topic",
|
||||||
targetType: "Topic",
|
isAssigned: this.topic.isAssigned(),
|
||||||
isAssigned: this.topic.isAssigned(),
|
onSuccess: () =>
|
||||||
})
|
|
||||||
.set("model.onSuccess", () => {
|
|
||||||
this.appEvents.trigger("post-stream:refresh", {
|
this.appEvents.trigger("post-stream:refresh", {
|
||||||
id: this.topic.postStream.firstPostId,
|
id: this.topic.postStream.firstPostId,
|
||||||
});
|
}),
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -195,7 +192,7 @@ function registerTopicFooterButtons(api) {
|
||||||
translatedLabel() {
|
translatedLabel() {
|
||||||
return I18n.t("discourse_assign.assign.title");
|
return I18n.t("discourse_assign.assign.title");
|
||||||
},
|
},
|
||||||
action() {
|
async action() {
|
||||||
if (!this.currentUser?.can_assign) {
|
if (!this.currentUser?.can_assign) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -205,16 +202,18 @@ function registerTopicFooterButtons(api) {
|
||||||
if (this.topic.isAssigned()) {
|
if (this.topic.isAssigned()) {
|
||||||
this.set("topic.assigned_to_user", null);
|
this.set("topic.assigned_to_user", null);
|
||||||
this.set("topic.assigned_to_group", null);
|
this.set("topic.assigned_to_group", null);
|
||||||
taskActions.unassign(this.topic.id, "Topic").then(() => {
|
|
||||||
this.appEvents.trigger("post-stream:refresh", {
|
await taskActions.unassign(this.topic.id, "Topic");
|
||||||
id: this.topic.postStream.firstPostId,
|
|
||||||
});
|
this.appEvents.trigger("post-stream:refresh", {
|
||||||
|
id: this.topic.postStream.firstPostId,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
taskActions.assign(this.topic).set("model.onSuccess", () => {
|
await taskActions.showAssignModal(this.topic, {
|
||||||
this.appEvents.trigger("post-stream:refresh", {
|
onSuccess: () =>
|
||||||
id: this.topic.postStream.firstPostId,
|
this.appEvents.trigger("post-stream:refresh", {
|
||||||
});
|
id: this.topic.postStream.firstPostId,
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -333,7 +332,7 @@ function registerTopicFooterButtons(api) {
|
||||||
`<span class="unassign-label"><span class="text">${label}</span></span>`
|
`<span class="unassign-label"><span class="text">${label}</span></span>`
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
action() {
|
async action() {
|
||||||
if (!this.currentUser?.can_assign) {
|
if (!this.currentUser?.can_assign) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -342,10 +341,11 @@ function registerTopicFooterButtons(api) {
|
||||||
|
|
||||||
this.set("topic.assigned_to_user", null);
|
this.set("topic.assigned_to_user", null);
|
||||||
this.set("topic.assigned_to_group", null);
|
this.set("topic.assigned_to_group", null);
|
||||||
taskActions.reassignUserToTopic(this.currentUser, this.topic).then(() => {
|
|
||||||
this.appEvents.trigger("post-stream:refresh", {
|
await taskActions.reassignUserToTopic(this.currentUser, this.topic);
|
||||||
id: this.topic.postStream.firstPostId,
|
|
||||||
});
|
this.appEvents.trigger("post-stream:refresh", {
|
||||||
|
id: this.topic.postStream.firstPostId,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
dropdown() {
|
dropdown() {
|
||||||
|
@ -382,23 +382,21 @@ function registerTopicFooterButtons(api) {
|
||||||
`<span class="unassign-label"><span class="text">${label}</span></span>`
|
`<span class="unassign-label"><span class="text">${label}</span></span>`
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
action() {
|
async action() {
|
||||||
if (!this.currentUser?.can_assign) {
|
if (!this.currentUser?.can_assign) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const taskActions = getOwner(this).lookup("service:task-actions");
|
const taskActions = getOwner(this).lookup("service:task-actions");
|
||||||
|
|
||||||
taskActions
|
await taskActions.showAssignModal(this.topic, {
|
||||||
.assign(this.topic, {
|
targetType: "Topic",
|
||||||
targetType: "Topic",
|
isAssigned: this.topic.isAssigned(),
|
||||||
isAssigned: this.topic.isAssigned(),
|
onSuccess: () =>
|
||||||
})
|
|
||||||
.set("model.onSuccess", () => {
|
|
||||||
this.appEvents.trigger("post-stream:refresh", {
|
this.appEvents.trigger("post-stream:refresh", {
|
||||||
id: this.topic.postStream.firstPostId,
|
id: this.topic.postStream.firstPostId,
|
||||||
});
|
}),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
dropdown() {
|
dropdown() {
|
||||||
return this.currentUser?.can_assign && this.topic.isAssigned();
|
return this.currentUser?.can_assign && this.topic.isAssigned();
|
||||||
|
@ -439,7 +437,7 @@ function initialize(api) {
|
||||||
},
|
},
|
||||||
before: "top",
|
before: "top",
|
||||||
});
|
});
|
||||||
if (api.getCurrentUser() && api.getCurrentUser().can_assign) {
|
if (api.getCurrentUser()?.can_assign) {
|
||||||
api.addPostMenuButton("assign", (post) => {
|
api.addPostMenuButton("assign", (post) => {
|
||||||
if (post.firstPost) {
|
if (post.firstPost) {
|
||||||
return;
|
return;
|
||||||
|
@ -465,13 +463,15 @@ function initialize(api) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
api.attachWidgetAction("post", "assignPost", function () {
|
api.attachWidgetAction("post", "assignPost", function () {
|
||||||
const taskActions = getOwner(this).lookup("service:task-actions");
|
const taskActions = getOwner(this).lookup("service:task-actions");
|
||||||
taskActions.assign(this.model, {
|
taskActions.showAssignModal(this.model, {
|
||||||
isAssigned: false,
|
isAssigned: false,
|
||||||
targetType: "Post",
|
targetType: "Post",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
api.attachWidgetAction("post", "unassignPost", function () {
|
api.attachWidgetAction("post", "unassignPost", function () {
|
||||||
const taskActions = getOwner(this).lookup("service:task-actions");
|
const taskActions = getOwner(this).lookup("service:task-actions");
|
||||||
taskActions.unassign(this.model.id, "Post").then(() => {
|
taskActions.unassign(this.model.id, "Post").then(() => {
|
||||||
|
@ -488,7 +488,7 @@ function initialize(api) {
|
||||||
});
|
});
|
||||||
|
|
||||||
api.addAdvancedSearchOptions(
|
api.addAdvancedSearchOptions(
|
||||||
api.getCurrentUser() && api.getCurrentUser().can_assign
|
api.getCurrentUser()?.can_assign
|
||||||
? {
|
? {
|
||||||
inOptionsForUsers: [
|
inOptionsForUsers: [
|
||||||
{
|
{
|
||||||
|
@ -914,25 +914,27 @@ export default {
|
||||||
});
|
});
|
||||||
|
|
||||||
TopicButtonAction.reopen({
|
TopicButtonAction.reopen({
|
||||||
assignUser: controller("assign-user"),
|
|
||||||
actions: {
|
actions: {
|
||||||
showReAssign() {
|
showReAssign() {
|
||||||
this.set("assignUser.isBulkAction", true);
|
const controller = getOwner(this).lookup("controller:bulk-assign");
|
||||||
this.set("assignUser.model", { username: "", note: "" });
|
controller.set("model", { username: "", note: "" });
|
||||||
this.send("changeBulkTemplate", "modal/assign-user");
|
this.send("changeBulkTemplate", "modal/bulk-assign");
|
||||||
},
|
},
|
||||||
|
|
||||||
unassignTopics() {
|
unassignTopics() {
|
||||||
this.performAndRefresh({ type: "unassign" });
|
this.performAndRefresh({ type: "unassign" });
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
addBulkButton("showReAssign", "assign", {
|
addBulkButton("showReAssign", "assign", {
|
||||||
icon: "user-plus",
|
icon: "user-plus",
|
||||||
class: "btn-default",
|
class: "btn-default assign-topics",
|
||||||
});
|
});
|
||||||
|
|
||||||
addBulkButton("unassignTopics", "unassign", {
|
addBulkButton("unassignTopics", "unassign", {
|
||||||
icon: "user-times",
|
icon: "user-times",
|
||||||
class: "btn-default",
|
class: "btn-default unassign-topics",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,38 @@
|
||||||
import Service from "@ember/service";
|
import Service, { inject as service } from "@ember/service";
|
||||||
import { ajax } from "discourse/lib/ajax";
|
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 {
|
export default class TaskActions extends Service {
|
||||||
i18nSuffix(targetType) {
|
@service modal;
|
||||||
switch (targetType) {
|
|
||||||
case "Post":
|
@tracked allowedGroups;
|
||||||
return "_post_modal";
|
@tracked allowedGroupsForAssignment;
|
||||||
case "Topic":
|
#suggestionsPromise;
|
||||||
return "_modal";
|
@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") {
|
unassign(targetId, targetType = "Topic") {
|
||||||
|
@ -22,19 +45,19 @@ export default class TaskActions extends Service {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
assign(target, options = { isAssigned: false, targetType: "Topic" }) {
|
showAssignModal(
|
||||||
return showModal("assign-user", {
|
target,
|
||||||
title:
|
{ isAssigned = false, targetType = "Topic", onSuccess }
|
||||||
"discourse_assign.assign" +
|
) {
|
||||||
this.i18nSuffix(options.targetType) +
|
return this.modal.show(AssignUser, {
|
||||||
`.${options.isAssigned ? "reassign_title" : "title"}`,
|
|
||||||
model: {
|
model: {
|
||||||
reassign: options.isAssigned,
|
reassign: isAssigned,
|
||||||
username: target.assigned_to_user?.username,
|
username: target.assigned_to_user?.username,
|
||||||
group_name: target.assigned_to_group?.name,
|
group_name: target.assigned_to_group?.name,
|
||||||
target,
|
|
||||||
targetType: options.targetType,
|
|
||||||
status: target.assignment_status,
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,76 +0,0 @@
|
||||||
<DModalBody class="assign">
|
|
||||||
<div>
|
|
||||||
<div class="control-group {{if this.assigneeError 'assignee-error'}}">
|
|
||||||
<label>{{i18n "discourse_assign.assign_modal.assignee_label"}}</label>
|
|
||||||
<AssigneeChooser
|
|
||||||
autocomplete="off"
|
|
||||||
@value={{this.assigneeName}}
|
|
||||||
@onChange={{this.assignUsername}}
|
|
||||||
autofocus="autofocus"
|
|
||||||
@showUserStatus={{true}}
|
|
||||||
@options={{hash
|
|
||||||
mobilePlacementStrategy="absolute"
|
|
||||||
filterPlaceholder=this.placeholderKey
|
|
||||||
includeGroups=true
|
|
||||||
customSearchOptions=(hash
|
|
||||||
assignableGroups=true defaultSearchResults=this.assignSuggestions
|
|
||||||
)
|
|
||||||
groupMembersOf=this.allowedGroups
|
|
||||||
maximum=1
|
|
||||||
autofocus=this.autofocus
|
|
||||||
tabindex=1
|
|
||||||
expandedOnInsert=(not this.assigneeName)
|
|
||||||
caretUpIcon="search"
|
|
||||||
caretDownIcon="search"
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{{#if this.assigneeError}}
|
|
||||||
<span class="error-label">
|
|
||||||
{{d-icon "exclamation-triangle"}}
|
|
||||||
{{i18n "discourse_assign.assign_modal.choose_assignee"}}
|
|
||||||
</span>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{#if this.statusEnabled}}
|
|
||||||
<div class="control-group assign-status">
|
|
||||||
<label>{{i18n "discourse_assign.assign_modal.status_label"}}</label>
|
|
||||||
<ComboBox
|
|
||||||
@id="assign-status"
|
|
||||||
@content={{this.availableStatuses}}
|
|
||||||
@value={{this.status}}
|
|
||||||
@onChange={{action (mut this.model.status)}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<div class="control-group assign-status">
|
|
||||||
<label>
|
|
||||||
{{i18n "discourse_assign.assign_modal.note_label"}} <span
|
|
||||||
class="label-optional"
|
|
||||||
>{{i18n "discourse_assign.assign_modal.optional_label"}}</span>
|
|
||||||
</label>
|
|
||||||
<Textarea
|
|
||||||
id="assign-modal-note"
|
|
||||||
@value={{this.model.note}}
|
|
||||||
{{! template-lint-disable no-down-event-binding }}
|
|
||||||
{{on "keydown" (action "handleTextAreaKeydown")}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</DModalBody>
|
|
||||||
|
|
||||||
<div class="modal-footer">
|
|
||||||
<DButton
|
|
||||||
@label={{if
|
|
||||||
this.model.reassign
|
|
||||||
"discourse_assign.reassign.title"
|
|
||||||
"discourse_assign.assign_modal.assign"
|
|
||||||
}}
|
|
||||||
@icon={{this.inviteIcon}}
|
|
||||||
@action={{this.assign}}
|
|
||||||
class="btn-primary"
|
|
||||||
@disabled={{this.disabled}}
|
|
||||||
/>
|
|
||||||
<DModalCancel @close={{route-action "closeModal"}} />
|
|
||||||
</div>
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
<div>
|
||||||
|
<AssignUserForm
|
||||||
|
@model={{this.model}}
|
||||||
|
@onSubmit={{this.assign}}
|
||||||
|
@formApi={{this.formApi}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<DButton
|
||||||
|
class="btn-primary"
|
||||||
|
@action={{this.formApi.submit}}
|
||||||
|
@label={{if
|
||||||
|
this.model.reassign
|
||||||
|
"discourse_assign.reassign.title"
|
||||||
|
"discourse_assign.assign_modal.assign"
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
|
@ -66,12 +66,12 @@
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.assign-user-modal {
|
.modal.assign {
|
||||||
.modal-inner-container {
|
.modal-inner-container {
|
||||||
width: 400px;
|
width: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.assign.modal-body {
|
.modal-body {
|
||||||
overflow-y: unset;
|
overflow-y: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,12 @@ import {
|
||||||
acceptance,
|
acceptance,
|
||||||
updateCurrentUser,
|
updateCurrentUser,
|
||||||
} from "discourse/tests/helpers/qunit-helpers";
|
} 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 { test } from "qunit";
|
||||||
|
import pretender, {
|
||||||
|
parsePostData,
|
||||||
|
response,
|
||||||
|
} from "discourse/tests/helpers/create-pretender";
|
||||||
|
|
||||||
acceptance("Discourse Assign | Assign mobile", function (needs) {
|
acceptance("Discourse Assign | Assign mobile", function (needs) {
|
||||||
needs.user();
|
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) {
|
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());
|
assert.true(menu.rowByValue("assign").exists());
|
||||||
await menu.selectRowByValue("assign");
|
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");
|
await visit("/t/internationalization-localization/280");
|
||||||
|
|
||||||
assert
|
assert
|
||||||
.dom("#post_2 .extra-buttons .d-icon-user-plus")
|
.dom("#post_2 .extra-buttons .d-icon-user-plus")
|
||||||
.doesNotExist("assign to post button is hidden");
|
.doesNotExist("assign to post button is hidden");
|
||||||
await click("#post_2 button.show-more-actions");
|
|
||||||
|
|
||||||
|
await click("#post_2 button.show-more-actions");
|
||||||
assert
|
assert
|
||||||
.dom("#post_2 .extra-buttons .d-icon-user-plus")
|
.dom("#post_2 .extra-buttons .d-icon-user-plus")
|
||||||
.exists("assign to post button exists");
|
.exists("assign to post button exists");
|
||||||
|
|
||||||
await click("#post_2 .extra-buttons .d-icon-user-plus");
|
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) {
|
test("Footer dropdown contains button", async function (assert) {
|
||||||
await visit("/t/internationalization-localization/280");
|
await visit("/t/internationalization-localization/280");
|
||||||
await click("#topic-footer-button-assign");
|
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) {
|
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");
|
await click("#topic-footer-button-assign");
|
||||||
|
|
||||||
assert
|
assert
|
||||||
.dom(".assign.modal-body #assign-status")
|
.dom(".assign.modal #assign-status")
|
||||||
.exists("assign status dropdown exists");
|
.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) {
|
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");
|
await click("#topic-footer-button-assign");
|
||||||
|
|
||||||
assert
|
assert
|
||||||
.dom(".assign.modal-body #assign-status")
|
.dom(".assign.modal #assign-status")
|
||||||
.doesNotExist("assign status dropdown doesn't exists");
|
.doesNotExist("assign status dropdown doesn't exists");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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");
|
||||||
|
});
|
||||||
|
});
|
|
@ -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`<GroupAssignedFilter @showAvatar={{true}} @filter={{filter}} />`,
|
||||||
|
|
||||||
|
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");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
|
@ -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);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -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"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
Loading…
Reference in New Issue