FEATURE: Assign Status (#363)

Adds a new plugin setting that when enabled adds a status field for every assignment. This setting defaults to off.

The possible status for an assignment are customizable via yet another new setting, and the first one on this list will be the default status for new assignments.

The status is not yet show anywhere except the assign modal and the small action posts in topics at the moment. Adding status to the assignment list for users and groups will be handled in the near future.


Co-authored-by: Penar Musaraj <pmusaraj@gmail.com>
This commit is contained in:
Rafael dos Santos Silva 2022-08-04 14:50:18 -03:00 committed by GitHub
parent a29a02abe7
commit 7a2fde72c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 475 additions and 32 deletions

View File

@ -56,6 +56,7 @@ module DiscourseAssign
username = params.permit(:username)["username"]
group_name = params.permit(:group_name)["group_name"]
note = params.permit(:note)["note"].presence
status = params.permit(:status)["status"].presence
assign_to =
(
@ -71,7 +72,7 @@ module DiscourseAssign
target = target_type.constantize.where(id: target_id).first
raise Discourse::NotFound unless target
assign = Assigner.new(target, current_user).assign(assign_to, note: note)
assign = Assigner.new(target, current_user).assign(assign_to, note: note, status: status)
if assign[:success]
render json: success_json

View File

@ -15,10 +15,26 @@ class Assignment < ActiveRecord::Base
)
}
before_validation :default_status
validate :validate_status, if: -> { SiteSetting.enable_assign_status }
def self.valid_type?(type)
VALID_TYPES.include?(type.downcase)
end
def self.statuses
SiteSetting.assign_statuses.split("|")
end
def self.default_status
Assignment.statuses.first
end
def self.status_enabled?
SiteSetting.enable_assign_status
end
def assigned_to_user?
assigned_to_type == "User"
end
@ -26,6 +42,18 @@ class Assignment < ActiveRecord::Base
def assigned_to_group?
assigned_to_type == "Group"
end
private
def default_status
self.status ||= Assignment.default_status if SiteSetting.enable_assign_status
end
def validate_status
if SiteSetting.enable_assign_status && !Assignment.statuses.include?(self.status)
errors.add(:status, :invalid)
end
end
end
# == Schema Information

View File

@ -1,6 +1,7 @@
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";
@ -42,6 +43,27 @@ export default Controller.extend(ModalFunctionality, {
});
},
@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(value, event) {
if ((event.ctrlKey || event.metaKey) && event.key === "Enter") {
@ -82,6 +104,7 @@ export default Controller.extend(ModalFunctionality, {
target_id: this.get("model.target.id"),
target_type: this.get("model.targetType"),
note: this.get("model.note"),
status: this.get("model.status"),
},
})
.then(() => {

View File

@ -577,7 +577,9 @@ function initialize(api) {
"assigned_group_to_post",
"unassigned_from_post",
"unassigned_group_from_post",
"details_change",
"note_change",
"status_change",
].includes(transformed.actionCode)
) {
transformed.isSmallAction = true;
@ -592,7 +594,8 @@ function initialize(api) {
topic.getProperties(
"assigned_to_user",
"assigned_to_group",
"assignment_note"
"assignment_note",
"assignment_status"
)
);
@ -617,6 +620,7 @@ function initialize(api) {
topicAssignee,
assignedToIndirectly.map((assigned) => ({
assignee: assigned.assigned_to,
status: assigned.assignment_status,
note: assigned.assignment_note,
}))
)
@ -782,6 +786,7 @@ function initialize(api) {
const target = post || topic;
target.set("assignment_note", data.assignment_note);
target.set("assignment_status", data.assignment_status);
if (data.assigned_type === "User") {
target.set(
"assigned_to_user_id",

View File

@ -38,6 +38,7 @@ export default Service.extend({
group_name: target.assigned_to_group?.name,
target,
targetType: options.targetType,
status: target.assignment_status,
},
});
},
@ -49,6 +50,7 @@ export default Service.extend({
username: user.username,
target_id: target.id,
target_type: targetType,
status: target.assignment_status,
},
});
},

View File

@ -25,9 +25,22 @@
</a>
{{/each}}
</div>
{{#if this.statusEnabled}}
<div class="control-group assign-status">
<label>{{i18n "discourse_assign.assign_modal.status_label"}}</label>
{{combo-box
id="assign-status"
content=availableStatuses
value=status
onChange=(action (mut model.status))
}}
</div>
{{/if}}
<div class="control-group assign-status">
<label>{{i18n "discourse_assign.assign_modal.note_label"}}</label>
{{textarea id="assign-modal-note" value=model.note key-down=(action "handleTextAreaKeydown")}}
</div>
</div>
{{/d-modal-body}}
<div class="modal-footer">

View File

@ -15,7 +15,9 @@ en:
unassigned_group_from_post: "unassigned %{who} from <a href='%{path}'>post</a> %{when}"
reassigned: "Reassigned %{who} %{when}"
reassigned_group: "Reassigned %{who} %{when}"
details_change: "changed assignment details for %{who} %{when}"
note_change: "changed assignment note for %{who} %{when}"
status_change: "changed assignment status for %{who} %{when}"
discourse_assign:
add_unassigned_filter: "Add 'unassigned' filter to category"
cant_act: "You cannot act on flags that have been assigned to other users"
@ -41,11 +43,11 @@ en:
title: "Unassign from Post"
help: "Unassign %{username} from Post"
reassign:
title: "Reassign"
title: "Edit"
title_w_ellipsis: "Edit assignment..."
to_self: "Reassign to me"
to_self_help: "Reassign Topic to me"
help: "Reassign Topic to a different user"
help: "Edit assignment details"
reassign_modal:
title: "Reassign Topic"
description: "Enter the name of the user you'd like to Reassign this topic"
@ -55,6 +57,7 @@ en:
description: "Enter the name of the user you'd like to assign this topic"
assign: "Assign"
note_label: Note
status_label: Status
assign_post_modal:
title: "Assign Post"
description: "Enter the name of the user you'd like to assign this post"

View File

@ -15,6 +15,13 @@ en:
remind_assigns_frequency: "Frequency for reminding users about assigned topics."
max_assigned_topics: "Maximum number of topics that can be assigned to a user."
assign_allowed_on_groups: "Users in these groups are allowed to assign topics and can be assigned topics."
enable_assign_status: "Add a customizable status field to every assignment."
assign_statuses: "List of statuses available to each assignment. The first status is the default status applied to every new assignment."
errors:
assign_statuses:
too_few: "There must be at least two different statuses available."
duplicate: "There are duplicate status values."
removed_in_use: "Can't remove a status from the list if there are existing assignments using this status."
discourse_assign:
assigned_to: "Topic assigned to @%{username}"
unassigned: "Topic was unassigned"
@ -77,3 +84,10 @@ en:
discourse_push_notifications:
popup:
assigned: "@%{username} assigned you"
activerecord:
errors:
models:
assignment:
attributes:
status:
invalid: "Selected status is invalid (it is not included in the assigned_status site setting)."

View File

@ -33,3 +33,12 @@ plugins:
default: ""
allow_any: false
refresh: true
enable_assign_status:
default: false
client: true
assign_statuses:
client: true
type: list
default: "New|In Progress|Done"
allow_any: true
validator: AssignStatusesValidator

View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
class AddStatusToAssignments < ActiveRecord::Migration[6.1]
def change
add_column :assignments, :status, :text, null: true
end
end

View File

@ -195,7 +195,7 @@ class ::Assigner
topic.posts.where(post_number: 1).first
end
def forbidden_reasons(assign_to:, type:, note:)
def forbidden_reasons(assign_to:, type:, note:, status:)
case
when assign_to.is_a?(User) && !can_assignee_see_target?(assign_to)
if topic.private_message?
@ -211,9 +211,9 @@ class ::Assigner
end
when !can_be_assigned?(assign_to)
assign_to.is_a?(User) ? :forbidden_assign_to : :forbidden_group_assign_to
when topic_same_assignee_and_note(assign_to, type, note)
when topic_same_assignee_and_details(assign_to, type, note, status)
assign_to.is_a?(User) ? :already_assigned : :group_already_assigned
when post_same_assignee_and_note(assign_to, type, note)
when post_same_assignee_and_details(assign_to, type, note, status)
assign_to.is_a?(User) ? :already_assigned : :group_already_assigned
when Assignment.where(topic: topic).count >= ASSIGNMENTS_PER_TOPIC_LIMIT
:too_many_assigns_for_topic
@ -222,32 +222,49 @@ class ::Assigner
end
end
def update_note(assign_to, note, skip_small_action_post: false)
@target.assignment.update!(note: note)
def update_details(assign_to, note, status, skip_small_action_post: false)
case
when @target.assignment.note != note && @target.assignment.status != status && status.present?
small_action_text = <<~TEXT
Status: #{@target.assignment.status} → #{status}
#{note}
TEXT
change_type = "details"
when @target.assignment.note != note
small_action_text = note
change_type = "note"
when @target.assignment.status != status
small_action_text = "#{@target.assignment.status}#{status}"
change_type = "status"
end
@target.assignment.update!(note: note, status: status)
queue_notification(assign_to, skip_small_action_post)
assignment = @target.assignment
publish_assignment(assignment, assign_to, note)
publish_assignment(assignment, assign_to, note, status)
# email is skipped, for now
unless skip_small_action_post
action_code = "note_change"
add_small_action_post(action_code, assign_to, note)
action_code = "#{change_type}_change"
add_small_action_post(action_code, assign_to, small_action_text)
end
{ success: true }
end
def assign(assign_to, note: nil, skip_small_action_post: false)
def assign(assign_to, note: nil, skip_small_action_post: false, status: nil)
assigned_to_type = assign_to.is_a?(User) ? "User" : "Group"
forbidden_reason = forbidden_reasons(assign_to: assign_to, type: assigned_to_type, note: note)
forbidden_reason =
forbidden_reasons(assign_to: assign_to, type: assigned_to_type, note: note, status: status)
return { success: false, reason: forbidden_reason } if forbidden_reason
if no_assignee_change?(assign_to)
return update_note(assign_to, note, skip_small_action_post: skip_small_action_post)
return update_details(assign_to, note, status, skip_small_action_post: skip_small_action_post)
end
action_code = {}
@ -265,13 +282,14 @@ class ::Assigner
assigned_by_user_id: @assigned_by.id,
topic_id: topic.id,
note: note,
status: status,
)
first_post.publish_change_to_clients!(:revised, reload_topic: true)
queue_notification(assign_to, skip_small_action_post)
publish_assignment(assignment, assign_to, note)
publish_assignment(assignment, assign_to, note, status)
if assignment.assigned_to_user?
if !TopicUser.exists?(
@ -413,6 +431,7 @@ class ::Assigner
post_number: post_target? && @target.post_number,
assigned_type: assignment.assigned_to.is_a?(User) ? "User" : "Group",
assignment_note: nil,
assignment_status: nil,
},
user_ids: allowed_user_ids,
)
@ -433,7 +452,7 @@ class ::Assigner
)
end
def add_small_action_post(action_code, assign_to, note)
def add_small_action_post(action_code, assign_to, text)
custom_fields = {
"action_code_who" => assign_to.is_a?(User) ? assign_to.username : assign_to.name,
}
@ -446,7 +465,7 @@ class ::Assigner
topic.add_moderator_post(
@assigned_by,
note,
text,
bump: false,
post_type: SiteSetting.assigns_public ? Post.types[:small_action] : Post.types[:whisper],
action_code: action_code,
@ -454,7 +473,7 @@ class ::Assigner
)
end
def publish_assignment(assignment, assign_to, note)
def publish_assignment(assignment, assign_to, note, status)
serializer = assignment.assigned_to_user? ? BasicUserSerializer : BasicGroupSerializer
MessageBus.publish(
"/staff/topic-assignment",
@ -466,6 +485,7 @@ class ::Assigner
assigned_type: assignment.assigned_to_type,
assigned_to: serializer.new(assign_to, scope: Guardian.new, root: false).as_json,
assignment_note: note,
assignment_status: status,
},
user_ids: allowed_user_ids,
)
@ -491,19 +511,27 @@ class ::Assigner
return "unassigned_group#{suffix}" if assignment.assigned_to_group?
end
def topic_same_assignee_and_note(assign_to, type, note)
def topic_same_assignee_and_details(assign_to, type, note, status)
topic.assignment&.assigned_to_id == assign_to.id &&
topic.assignment&.assigned_to_type == type && topic.assignment.active == true &&
topic.assignment&.note == note
topic.assignment&.note == note &&
(
topic.assignment&.status == status ||
topic.assignment&.status == Assignment.default_status && status.nil?
)
end
def post_same_assignee_and_note(assign_to, type, note)
def post_same_assignee_and_details(assign_to, type, note, status)
@target.is_a?(Topic) &&
Assignment
.where(topic_id: topic.id, target_type: "Post", active: true)
.any? do |assignment|
assignment.assigned_to_id == assign_to.id && assignment.assigned_to_type == type &&
assignment&.note == note
assignment&.note == note &&
(
topic.assignment&.status == status ||
topic.assignment&.status == Assignment.default_status && status.nil?
)
end
end

View File

@ -32,6 +32,7 @@ module DiscourseAssign
.map do |post_id, assigned_map|
assigned_to = assigned_map[:assigned_to]
note = assigned_map[:assignment_note]
status = assigned_map[:assignment_status]
post_number = assigned_map[:post_number]
if (assigned_to.is_a?(User))
@ -41,6 +42,7 @@ module DiscourseAssign
assigned_to: build_assigned_to_user(assigned_to, topic),
post_number: post_number,
assignment_note: note,
assignment_status: status,
},
]
elsif assigned_to.is_a?(Group)
@ -50,6 +52,7 @@ module DiscourseAssign
assigned_to: build_assigned_to_group(assigned_to, topic),
post_number: post_number,
assignment_note: note,
assignment_status: status,
},
]
end

View File

@ -0,0 +1,29 @@
# frozen_string_literal: true
class AssignStatusesValidator
def initialize(opts = {})
@opts = opts
end
def valid_value?(value)
statuses = value.split("|")
case
when statuses.size < 2
@reason = "too_few"
return false
when statuses.size != statuses.uniq.size
@reason = "duplicate"
return false
when Assignment.where.not(status: statuses).count > 0
@reason = "removed_in_use"
return false
end
true
end
def error_message
I18n.t("site_settings.errors.assign_statuses.#{@reason}")
end
end

View File

@ -19,6 +19,7 @@ register_svg_icon "user-times"
load File.expand_path("../lib/discourse_assign/engine.rb", __FILE__)
load File.expand_path("../lib/discourse_assign/helpers.rb", __FILE__)
load File.expand_path("../lib/validators/assign_statuses_validator.rb", __FILE__)
Discourse::Application.routes.append do
mount ::DiscourseAssign::Engine, at: "/assign"
@ -497,11 +498,16 @@ after_initialize do
.where(topic_id: id, target_type: "Post", active: true)
.includes(:target)
.inject({}) do |acc, assignment|
if assignment.target
acc[assignment.target_id] = {
assigned_to: assignment.assigned_to,
post_number: assignment.target.post_number,
assignment_note: assignment.note,
} if assignment.target
}
acc[assignment.target_id][
:assignment_status
] = assignment.status if SiteSetting.enable_assign_status
end
acc
end
end
@ -563,6 +569,13 @@ after_initialize do
(SiteSetting.assigns_public || scope.can_assign?) && object.topic.assignment.present?
end
add_to_serializer(:topic_view, :assignment_status, false) { object.topic.assignment.status }
add_to_serializer(:topic_view, :include_assignment_status?, false) do
SiteSetting.enable_assign_status && (SiteSetting.assigns_public || scope.can_assign?) &&
object.topic.assignment.present?
end
# SuggestedTopic serializer
add_to_serializer(:suggested_topic, :assigned_to_user, false) do
DiscourseAssign::Helpers.build_assigned_to_user(object.assigned_to, object)
@ -613,6 +626,13 @@ after_initialize do
(SiteSetting.assigns_public || scope.can_assign?) && object.assigned_to&.is_a?(Group)
end
add_to_serializer(:topic_list_item, :assignment_status, false) { object.assignment.status }
add_to_serializer(:topic_list_item, :include_assignment_status?, false) do
SiteSetting.enable_assign_status && (SiteSetting.assigns_public || scope.can_assign?) &&
object.assignment.present?
end
# SearchTopicListItem serializer
add_to_serializer(:search_topic_list_item, :assigned_to_user, false) do
DiscourseAssign::Helpers.build_assigned_to_user(object.assigned_to, object)
@ -718,6 +738,13 @@ after_initialize do
(SiteSetting.assigns_public || scope.can_assign?) && object.assignment.present?
end
add_to_serializer(:post, :assignment_status, false) { object.assignment.status }
add_to_serializer(:post, :include_assignment_status?, false) do
SiteSetting.enable_assign_status && (SiteSetting.assigns_public || scope.can_assign?) &&
object.assignment.present?
end
# CurrentUser serializer
add_to_serializer(:current_user, :can_assign) { object.can_assign? }

View File

@ -69,6 +69,7 @@ describe Search do
},
post_number: post5.post_number,
assignment_note: nil,
assignment_status: nil,
},
)
end

View File

@ -3,7 +3,10 @@
require "rails_helper"
RSpec.describe Assigner do
before { SiteSetting.assign_enabled = true }
before do
SiteSetting.assign_enabled = true
SiteSetting.enable_assign_status = true
end
let(:assign_allowed_group) { Group.find_by(name: "staff") }
let(:pm_post) { Fabricate(:private_message_post) }
@ -53,11 +56,17 @@ RSpec.describe Assigner do
expect(topic.posts.last.raw).to eq "tomtom best mom"
end
it "can assign with status" do
assigner.assign(moderator, status: "In Progress")
expect(topic.assignment.status).to eq "In Progress"
end
it "publishes topic assignment after assign and unassign" do
messages =
MessageBus.track_publish("/staff/topic-assignment") do
assigner = described_class.new(topic, moderator_2)
assigner.assign(moderator, note: "tomtom best mom")
assigner.assign(moderator, note: "tomtom best mom", status: "In Progress")
assigner.unassign
end
@ -71,6 +80,7 @@ RSpec.describe Assigner do
assigned_type: "User",
assigned_to: BasicUserSerializer.new(moderator, scope: Guardian.new, root: false).as_json,
assignment_note: "tomtom best mom",
assignment_status: "In Progress",
},
)
@ -83,6 +93,7 @@ RSpec.describe Assigner do
post_number: false,
assigned_type: "User",
assignment_note: nil,
assignment_status: nil,
},
)
end
@ -356,6 +367,76 @@ RSpec.describe Assigner do
expect(small_action_post.action_code).to eq "note_change"
end
end
describe "updating status" do
it "does not recreate assignment if no assignee change" do
assigner.assign(moderator)
expect do assigner.assign(moderator, status: "Done") end.to_not change {
Assignment.last.id
}
end
it "updates status" do
assigner.assign(moderator)
assigner.assign(moderator, status: "Done")
expect(Assignment.last.status).to eq "Done"
end
it "queues notification" do
assigner.assign(moderator)
expect_enqueued_with(job: :assign_notification) do
assigner.assign(moderator, status: "Done")
end
end
it "publishes topic assignment with note" do
assigner.assign(moderator)
messages =
MessageBus.track_publish("/staff/topic-assignment") do
assigner = described_class.new(topic, moderator_2)
assigner.assign(moderator, status: "Done")
end
expect(messages[0].channel).to eq "/staff/topic-assignment"
expect(messages[0].data).to include(
{
type: "assigned",
topic_id: topic.id,
post_id: false,
post_number: false,
assigned_type: "User",
assigned_to:
BasicUserSerializer.new(moderator, scope: Guardian.new, root: false).as_json,
assignment_status: "Done",
},
)
end
it "adds a note_change small action post" do
assigner.assign(moderator)
assigner.assign(moderator, status: "Done")
small_action_post = topic.posts.last
expect(small_action_post.action_code).to eq "status_change"
end
end
describe "updating note and status at the same time" do
it "adds a note_change small action post" do
assigner.assign(moderator)
assigner.assign(moderator, note: "This is a note!", status: "Done")
small_action_post = topic.posts.last
expect(small_action_post.action_code).to eq "details_change"
end
end
end
context "assign_self_regex" do

View File

@ -115,6 +115,7 @@ RSpec.describe DiscourseAssign::AssignController do
before do
sign_in(user)
add_to_assign_allowed_group(user2)
SiteSetting.enable_assign_status = true
end
it "assigns topic to a user" do
@ -141,6 +142,29 @@ RSpec.describe DiscourseAssign::AssignController do
expect(post.topic.reload.assignment.note).to eq("do dis pls")
end
it "assigns topic with a set status to a user" do
put "/assign/assign.json",
params: {
target_id: post.topic_id,
target_type: "Topic",
username: user2.username,
status: "In Progress",
}
expect(post.topic.reload.assignment.status).to eq("In Progress")
end
it "assigns topic with default status to a user" do
put "/assign/assign.json",
params: {
target_id: post.topic_id,
target_type: "Topic",
username: user2.username,
}
expect(post.topic.reload.assignment.status).to eq("New")
end
it "assigns topic to a group" do
put "/assign/assign.json",
params: {

View File

@ -39,4 +39,24 @@ describe PostSerializer do
serializer = PostSerializer.new(post, scope: guardian)
expect(serializer.as_json[:post][:assignment_note]).to eq("tomtom best")
end
context "when status is enabled" do
before { SiteSetting.enable_assign_status = true }
it "includes status in serializer" do
Assigner.new(post, user).assign(user, status: "Done")
serializer = PostSerializer.new(post, scope: guardian)
expect(serializer.as_json[:post][:assignment_status]).to eq("Done")
end
end
context "when status is disabled" do
before { SiteSetting.enable_assign_status = false }
it "doesn't include status in serializer" do
Assigner.new(post, user).assign(user, status: "Done")
serializer = PostSerializer.new(post, scope: guardian)
expect(serializer.as_json[:post][:assignment_status]).not_to eq("Done")
end
end
end

View File

@ -45,4 +45,40 @@ RSpec.describe TopicViewSerializer do
serializer.as_json[:topic_view][:indirectly_assigned_to][post.id][:assignment_note],
).to eq("note me down")
end
context "when status is enabled" do
before { SiteSetting.enable_assign_status = true }
it "includes status in serializer" do
Assigner.new(topic, user).assign(user, status: "Done")
serializer = TopicViewSerializer.new(TopicView.new(topic), scope: guardian)
expect(serializer.as_json[:topic_view][:assignment_status]).to eq("Done")
end
it "includes indirectly_assigned_to status in serializer" do
Assigner.new(post, user).assign(user, status: "Done")
serializer = TopicViewSerializer.new(TopicView.new(topic), scope: guardian)
expect(
serializer.as_json[:topic_view][:indirectly_assigned_to][post.id][:assignment_status],
).to eq("Done")
end
end
context "when status is disabled" do
before { SiteSetting.enable_assign_status = false }
it "doesn't include status in serializer" do
Assigner.new(topic, user).assign(user, status: "Done")
serializer = TopicViewSerializer.new(TopicView.new(topic), scope: guardian)
expect(serializer.as_json[:topic_view][:assignment_status]).not_to eq("Done")
end
it "doesn't include indirectly_assigned_to status in serializer" do
Assigner.new(post, user).assign(user, status: "Done")
serializer = TopicViewSerializer.new(TopicView.new(topic), scope: guardian)
expect(
serializer.as_json[:topic_view][:indirectly_assigned_to][post.id][:assignment_status],
).not_to eq("Done")
end
end
end

View File

@ -109,6 +109,92 @@ acceptance("Discourse Assign | Assign desktop", function (needs) {
});
});
acceptance("Discourse Assign | Assign Status enabled", function (needs) {
needs.user({
can_assign: true,
});
needs.settings({
assign_enabled: true,
enable_assign_status: true,
assign_statuses: "New|In Progress|Done",
});
needs.hooks.beforeEach(() => clearTopicFooterButtons());
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",
},
],
});
});
server.put("/assign/assign", () => {
return helper.response({ success: true });
});
});
test("Modal contains status dropdown", async (assert) => {
await visit("/t/internationalization-localization/280");
await click("#topic-footer-button-assign");
assert.ok(
exists(".assign.modal-body #assign-status"),
"assign status dropdown exists"
);
});
});
acceptance("Discourse Assign | Assign Status disabled", function (needs) {
needs.user({
can_assign: true,
});
needs.settings({ assign_enabled: true, enable_assign_status: false });
needs.hooks.beforeEach(() => clearTopicFooterButtons());
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",
},
],
});
});
server.put("/assign/assign", () => {
return helper.response({ success: true });
});
});
test("Modal contains status dropdown", async (assert) => {
await visit("/t/internationalization-localization/280");
await click("#topic-footer-button-assign");
assert.notOk(
exists(".assign.modal-body #assign-status"),
"assign status dropdown doesn't exists"
);
});
});
// See RemindAssignsFrequencySiteSettings
const remindersFrequency = [
{

View File

@ -23,6 +23,7 @@ function assignCurrentUserToTopic(needs) {
"/letter_avatar/eviltrout/{size}/3_f9720745f5ce6dfc2b5641fca999d934.png",
};
topic["assignment_note"] = "Shark Doododooo";
topic["assignment_status"] = "New";
topic["indirectly_assigned_to"] = {
2: {
assigned_to: {
@ -89,6 +90,7 @@ acceptance("Discourse Assign | Assigned topic", function (needs) {
tagging_enabled: true,
assigns_user_url_path: "/",
assigns_public: true,
enable_assign_status: true,
});
assignCurrentUserToTopic(needs);

View File

@ -28,6 +28,7 @@ discourseModule("Unit | Service | task-actions", function () {
group_name: "cats",
target,
targetType: "Topic",
status: undefined,
},
});
});