FEATURE: better UI for group assignments (#197)
On the group assignment page, we should be able to display - all topics assigned to that group + to user belonging to that group - all topics assigned directly to group - all topics assigned to individual user
This commit is contained in:
parent
da0a6ce219
commit
62d87f0084
|
@ -126,17 +126,25 @@ module DiscourseAssign
|
||||||
SQL
|
SQL
|
||||||
end
|
end
|
||||||
|
|
||||||
assignment_count = Topic
|
group_assignment_count = Topic
|
||||||
.joins("JOIN assignments a ON a.topic_id = topics.id AND a.assigned_to_id IS NOT NULL ")
|
.joins("JOIN assignments a ON a.topic_id = topics.id")
|
||||||
.where(<<~SQL, group_id: group.id)
|
.where(<<~SQL, group_id: group.id)
|
||||||
(a.assigned_to_id IN (SELECT group_users.user_id FROM group_users WHERE group_id = :group_id) AND a.assigned_to_type = 'User')
|
a.assigned_to_id = :group_id AND a.assigned_to_type = 'Group'
|
||||||
OR
|
|
||||||
(a.assigned_to_id = :group_id AND a.assigned_to_type = 'Group')
|
|
||||||
SQL
|
SQL
|
||||||
.where("topics.deleted_at IS NULL")
|
|
||||||
.count
|
.count
|
||||||
|
|
||||||
render json: { members: serialize_data(members, GroupUserAssignedSerializer), assignment_count: assignment_count }
|
assignment_count = Topic
|
||||||
|
.joins("JOIN assignments a ON a.topic_id = topics.id")
|
||||||
|
.joins("JOIN group_users ON group_users.user_id = a.assigned_to_id ")
|
||||||
|
.where("group_users.group_id = ?", group.id)
|
||||||
|
.where("a.assigned_to_type = 'User'")
|
||||||
|
.count
|
||||||
|
|
||||||
|
render json: {
|
||||||
|
members: serialize_data(members, GroupUserAssignedSerializer),
|
||||||
|
assignment_count: assignment_count + group_assignment_count,
|
||||||
|
group_assignment_count: group_assignment_count
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -38,6 +38,7 @@ export default UserTopicsList.extend({
|
||||||
order: this.order,
|
order: this.order,
|
||||||
ascending: this.ascending,
|
ascending: this.ascending,
|
||||||
search: this.search,
|
search: this.search,
|
||||||
|
direct: this.model.params.direct,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.then((result) => this.set("model", result))
|
.then((result) => this.set("model", result))
|
||||||
|
|
|
@ -13,12 +13,14 @@ export default DiscourseRoute.extend({
|
||||||
|
|
||||||
model(params) {
|
model(params) {
|
||||||
let filter = null;
|
let filter = null;
|
||||||
if (params.filter !== "everyone") {
|
if (
|
||||||
filter = `topics/messages-assigned/${params.filter}`;
|
["everyone", this.modelFor("group").get("name")].includes(params.filter)
|
||||||
} else {
|
) {
|
||||||
filter = `topics/group-topics-assigned/${this.modelFor("group").get(
|
filter = `topics/group-topics-assigned/${this.modelFor("group").get(
|
||||||
"name"
|
"name"
|
||||||
)}`;
|
)}`;
|
||||||
|
} else {
|
||||||
|
filter = `topics/messages-assigned/${params.filter}`;
|
||||||
}
|
}
|
||||||
const lastTopicList = findOrResetCachedTopicList(this.session, filter);
|
const lastTopicList = findOrResetCachedTopicList(this.session, filter);
|
||||||
return lastTopicList
|
return lastTopicList
|
||||||
|
@ -29,6 +31,7 @@ export default DiscourseRoute.extend({
|
||||||
order: params.order,
|
order: params.order,
|
||||||
ascending: params.ascending,
|
ascending: params.ascending,
|
||||||
search: params.search,
|
search: params.search,
|
||||||
|
direct: params.filter !== "everyone",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -13,7 +13,10 @@ export default DiscourseRoute.extend({
|
||||||
members: [],
|
members: [],
|
||||||
group: this.modelFor("group"),
|
group: this.modelFor("group"),
|
||||||
});
|
});
|
||||||
controller.group.set("assignment_count", model.assignment_count);
|
controller.group.setProperties({
|
||||||
|
assignment_count: model.assignment_count,
|
||||||
|
group_assignment_count: model.group_assignment_count,
|
||||||
|
});
|
||||||
|
|
||||||
controller.findMembers(true);
|
controller.findMembers(true);
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
{{#if show-avatar}}
|
{{#if showAvatar}}
|
||||||
{{#link-to "group.assigned.show" filter.username_lower (query-params order=order ascending=ascending search=search)}}
|
{{#link-to "group.assigned.show" filter.username_lower (query-params order=order ascending=ascending search=search)}}
|
||||||
<div class="assign-image">
|
<div class="assign-image">
|
||||||
<a href={{filter.userPath}} data-user-card={{filter.username}}>{{avatar filter imageSize="large"}}</a>
|
<a href={{filter.userPath}} data-user-card={{filter.username}}>{{avatar filter imageSize="large"}}</a>
|
||||||
|
@ -13,6 +13,19 @@
|
||||||
{{filter.assignments_count}}
|
{{filter.assignments_count}}
|
||||||
</div>
|
</div>
|
||||||
{{/link-to}}
|
{{/link-to}}
|
||||||
|
{{else if groupName}}
|
||||||
|
{{#link-to "group.assigned.show" filter (query-params order=order ascending=ascending search=search)}}
|
||||||
|
<div class="assign-image">
|
||||||
|
{{d-icon "group-plus"}}
|
||||||
|
</div>
|
||||||
|
<div class="assign-names">
|
||||||
|
<div class="assign-username">{{groupName}}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="assign-count">
|
||||||
|
{{assignmentCount}}
|
||||||
|
</div>
|
||||||
|
{{/link-to}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{#link-to "group.assigned.show" filter (query-params order=order ascending=ascending search=search)}}
|
{{#link-to "group.assigned.show" filter (query-params order=order ascending=ascending search=search)}}
|
||||||
<div class="assign-everyone">
|
<div class="assign-everyone">
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#load-more selector=".activity-nav li" action=(action "loadMore")}}
|
{{#load-more selector=".activity-nav li" action=(action "loadMore")}}
|
||||||
{{group-assigned-filter
|
{{group-assigned-filter
|
||||||
show-avatar=false
|
showAvatar=false
|
||||||
filter="everyone"
|
filter="everyone"
|
||||||
routeType=route_type
|
routeType=route_type
|
||||||
assignmentCount=group.assignment_count
|
assignmentCount=group.assignment_count
|
||||||
|
@ -21,9 +21,19 @@
|
||||||
ascending=ascending
|
ascending=ascending
|
||||||
order=order
|
order=order
|
||||||
}}
|
}}
|
||||||
|
{{group-assigned-filter
|
||||||
|
showAvatar=false
|
||||||
|
groupName=group.name
|
||||||
|
filter=group.name
|
||||||
|
routeType=route_type
|
||||||
|
assignmentCount=group.group_assignment_count
|
||||||
|
search=search
|
||||||
|
ascending=ascending
|
||||||
|
order=order
|
||||||
|
}}
|
||||||
{{#each members as |member|}}
|
{{#each members as |member|}}
|
||||||
{{group-assigned-filter
|
{{group-assigned-filter
|
||||||
show-avatar=true
|
showAvatar=true
|
||||||
filter=member
|
filter=member
|
||||||
routeType=route_type
|
routeType=route_type
|
||||||
search=search
|
search=search
|
||||||
|
|
|
@ -112,6 +112,16 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0.5em 13px;
|
padding: 0.5em 13px;
|
||||||
|
|
||||||
|
.assign-image {
|
||||||
|
width: 45px;
|
||||||
|
margin-right: 0.5em;
|
||||||
|
text-align: center;
|
||||||
|
svg {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.assign-names {
|
.assign-names {
|
||||||
font-size: $font-down-1;
|
font-size: $font-down-1;
|
||||||
grid-area: names;
|
grid-area: names;
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AddIndexToAssignedToIdAndType < ActiveRecord::Migration[6.1]
|
||||||
|
def change
|
||||||
|
add_index :assignments, [:assigned_to_id, :assigned_to_type]
|
||||||
|
end
|
||||||
|
end
|
50
plugin.rb
50
plugin.rb
|
@ -275,34 +275,48 @@ after_initialize do
|
||||||
add_to_class(:topic_query, :list_messages_assigned) do |user|
|
add_to_class(:topic_query, :list_messages_assigned) do |user|
|
||||||
list = default_results(include_pms: true)
|
list = default_results(include_pms: true)
|
||||||
|
|
||||||
list = list.where(<<~SQL, user_id: user.id)
|
topic_ids_sql = +<<~SQL
|
||||||
topics.id IN (
|
SELECT topic_id FROM assignments
|
||||||
SELECT topic_id FROM assignments
|
LEFT JOIN group_users ON group_users.user_id = :user_id
|
||||||
LEFT JOIN group_users ON group_users.user_id = :user_id
|
WHERE
|
||||||
WHERE
|
assigned_to_id = :user_id AND assigned_to_type = 'User'
|
||||||
assigned_to_id = :user_id AND assigned_to_type = 'User'
|
|
||||||
OR
|
|
||||||
assigned_to_id IN (group_users.group_id) AND assigned_to_type = 'Group'
|
|
||||||
)
|
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
|
if @options[:filter] != :direct
|
||||||
|
topic_ids_sql << <<~SQL
|
||||||
|
OR assigned_to_id IN (group_users.group_id) AND assigned_to_type = 'Group'
|
||||||
|
SQL
|
||||||
|
end
|
||||||
|
|
||||||
|
sql = "topics.id IN (#{topic_ids_sql})"
|
||||||
|
|
||||||
|
list = list.where(sql, user_id: user.id)
|
||||||
|
|
||||||
create_list(:assigned, { unordered: true }, list)
|
create_list(:assigned, { unordered: true }, list)
|
||||||
end
|
end
|
||||||
|
|
||||||
add_to_class(:topic_query, :list_group_topics_assigned) do |group|
|
add_to_class(:topic_query, :list_group_topics_assigned) do |group|
|
||||||
list = default_results(include_pms: true)
|
list = default_results(include_pms: true)
|
||||||
|
|
||||||
list = list.where(<<~SQL, group_id: group.id)
|
topic_ids_sql = +<<~SQL
|
||||||
topics.id IN (
|
SELECT topic_id FROM assignments
|
||||||
SELECT topic_id FROM assignments
|
WHERE (
|
||||||
WHERE (
|
assigned_to_id = :group_id AND assigned_to_type = 'Group'
|
||||||
assigned_to_id IN (SELECT user_id from group_users where group_id = :group_id) AND assigned_to_type = 'User'
|
|
||||||
) OR (
|
|
||||||
assigned_to_id = :group_id AND assigned_to_type = 'Group'
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
|
if @options[:filter] != :direct
|
||||||
|
topic_ids_sql << <<~SQL
|
||||||
|
OR (
|
||||||
|
assigned_to_id IN (SELECT user_id from group_users where group_id = :group_id) AND assigned_to_type = 'User'
|
||||||
|
)
|
||||||
|
SQL
|
||||||
|
end
|
||||||
|
|
||||||
|
sql = "topics.id IN (#{topic_ids_sql})"
|
||||||
|
|
||||||
|
list = list.where(sql, group_id: group.id)
|
||||||
|
|
||||||
create_list(:assigned, { unordered: true }, list)
|
create_list(:assigned, { unordered: true }, list)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -337,6 +351,7 @@ after_initialize do
|
||||||
raise Discourse::InvalidAccess unless current_user.can_assign?
|
raise Discourse::InvalidAccess unless current_user.can_assign?
|
||||||
|
|
||||||
list_opts = build_topic_list_options
|
list_opts = build_topic_list_options
|
||||||
|
list_opts.merge!({ filter: :direct }) if params[:direct] == "true"
|
||||||
list = generate_list_for("messages_assigned", user, list_opts)
|
list = generate_list_for("messages_assigned", user, list_opts)
|
||||||
|
|
||||||
list.more_topics_url = construct_url_with(:next, list_opts)
|
list.more_topics_url = construct_url_with(:next, list_opts)
|
||||||
|
@ -354,6 +369,7 @@ after_initialize do
|
||||||
raise Discourse::InvalidAccess unless group.can_show_assigned_tab?
|
raise Discourse::InvalidAccess unless group.can_show_assigned_tab?
|
||||||
|
|
||||||
list_opts = build_topic_list_options
|
list_opts = build_topic_list_options
|
||||||
|
list_opts.merge!({ filter: :direct }) if params[:direct] == "true"
|
||||||
list = generate_list_for("group_topics_assigned", group, list_opts)
|
list = generate_list_for("group_topics_assigned", group, list_opts)
|
||||||
|
|
||||||
list.more_topics_url = construct_url_with(:next, list_opts)
|
list.more_topics_url = construct_url_with(:next, list_opts)
|
||||||
|
|
|
@ -8,8 +8,8 @@ describe TopicQuery do
|
||||||
SiteSetting.assign_enabled = true
|
SiteSetting.assign_enabled = true
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:user) { Fabricate(:user) }
|
fab!(:user) { Fabricate(:user) }
|
||||||
let(:user2) { Fabricate(:user) }
|
fab!(:user2) { Fabricate(:user) }
|
||||||
|
|
||||||
include_context 'A group that is allowed to assign'
|
include_context 'A group that is allowed to assign'
|
||||||
|
|
||||||
|
@ -19,56 +19,73 @@ describe TopicQuery do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#list_messages_assigned' do
|
describe '#list_messages_assigned' do
|
||||||
before do
|
fab!(:private_message) { Fabricate(:private_message_topic, user: user) }
|
||||||
@private_message = Fabricate(:private_message_topic, user: user)
|
fab!(:topic) { Fabricate(:topic, user: user) }
|
||||||
@topic = Fabricate(:topic, user: user)
|
fab!(:group_topic) { Fabricate(:topic, user: user) }
|
||||||
|
|
||||||
assign_to(@private_message, user)
|
before do
|
||||||
assign_to(@topic, user)
|
assign_to(private_message, user, user)
|
||||||
|
assign_to(topic, user, user)
|
||||||
|
assign_to(group_topic, user, assign_allowed_group)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'Includes topics and PMs assigned to user' do
|
it 'Includes topics and PMs assigned to user' do
|
||||||
assigned_messages = TopicQuery.new(user, { page: 0 }).list_messages_assigned(user).topics
|
assigned_messages = TopicQuery.new(user, { page: 0 }).list_messages_assigned(user).topics
|
||||||
|
|
||||||
expect(assigned_messages).to contain_exactly(@private_message, @topic)
|
expect(assigned_messages).to contain_exactly(private_message, topic, group_topic)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'Excludes topics and PMs not assigned to user' do
|
it 'Excludes topics and PMs not assigned to user' do
|
||||||
assigned_messages = TopicQuery.new(user2, { page: 0 }).list_messages_assigned(user2).topics
|
assigned_messages = TopicQuery.new(user2, { page: 0 }).list_messages_assigned(user2).topics
|
||||||
|
|
||||||
expect(assigned_messages).to be_empty
|
expect(assigned_messages).to contain_exactly(group_topic)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'direct filter excludes group assignment' do
|
||||||
|
assigned_messages = TopicQuery.new(user, { page: 0, filter: :direct }).list_messages_assigned(user).topics
|
||||||
|
|
||||||
|
expect(assigned_messages).to contain_exactly(private_message, topic)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'Returns the results ordered by the bumped_at field' do
|
it 'Returns the results ordered by the bumped_at field' do
|
||||||
@topic.update(bumped_at: 2.weeks.ago)
|
topic.update(bumped_at: 2.weeks.ago)
|
||||||
|
|
||||||
assigned_messages = TopicQuery.new(user, { page: 0 }).list_messages_assigned(user).topics
|
assigned_messages = TopicQuery.new(user, { page: 0 }).list_messages_assigned(user).topics
|
||||||
|
|
||||||
expect(assigned_messages).to eq([@private_message, @topic])
|
expect(assigned_messages).to eq([group_topic, private_message, topic])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#list_group_topics_assigned' do
|
describe '#list_group_topics_assigned' do
|
||||||
before do
|
|
||||||
@private_message = Fabricate(:private_message_topic, user: user)
|
|
||||||
@topic = Fabricate(:topic, user: user)
|
|
||||||
|
|
||||||
assign_to(@private_message, user)
|
fab!(:private_message) { Fabricate(:private_message_topic, user: user) }
|
||||||
assign_to(@topic, user2)
|
fab!(:topic) { Fabricate(:topic, user: user) }
|
||||||
|
fab!(:group_topic) { Fabricate(:topic, user: user) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
assign_to(private_message, user, user)
|
||||||
|
assign_to(topic, user2, user2)
|
||||||
|
assign_to(group_topic, user, assign_allowed_group)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'Includes topics and PMs assigned to user' do
|
it 'Includes topics and PMs assigned to user' do
|
||||||
assigned_messages = TopicQuery.new(user, { page: 0 }).list_group_topics_assigned(assign_allowed_group).topics
|
assigned_messages = TopicQuery.new(user, { page: 0 }).list_group_topics_assigned(assign_allowed_group).topics
|
||||||
|
|
||||||
expect(assigned_messages).to contain_exactly(@private_message, @topic)
|
expect(assigned_messages).to contain_exactly(private_message, topic, group_topic)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'Returns the results ordered by the bumped_at field' do
|
it 'Returns the results ordered by the bumped_at field' do
|
||||||
@topic.update(bumped_at: 2.weeks.ago)
|
topic.update(bumped_at: 2.weeks.ago)
|
||||||
|
|
||||||
assigned_messages = TopicQuery.new(user, { page: 0 }).list_group_topics_assigned(assign_allowed_group).topics
|
assigned_messages = TopicQuery.new(user, { page: 0 }).list_group_topics_assigned(assign_allowed_group).topics
|
||||||
|
|
||||||
expect(assigned_messages).to eq([@private_message, @topic])
|
expect(assigned_messages).to eq([group_topic, private_message, topic])
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'direct filter shows only group assignments' do
|
||||||
|
assigned_messages = TopicQuery.new(user, { page: 0, filter: :direct }).list_group_topics_assigned(assign_allowed_group).topics
|
||||||
|
|
||||||
|
expect(assigned_messages).to contain_exactly(group_topic)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -92,7 +109,7 @@ describe TopicQuery do
|
||||||
Fabricate.build(:topic_allowed_user, user: user2)
|
Fabricate.build(:topic_allowed_user, user: user2)
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
assign_to(topic, user)
|
assign_to(topic, user, user)
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:group2) { Fabricate(:group) }
|
let(:group2) { Fabricate(:group) }
|
||||||
|
@ -106,7 +123,7 @@ describe TopicQuery do
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
assign_to(topic, user)
|
assign_to(topic, user, user)
|
||||||
end
|
end
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
@ -178,7 +195,7 @@ describe TopicQuery do
|
||||||
|
|
||||||
it "filters topics assigned to anybody (*)" do
|
it "filters topics assigned to anybody (*)" do
|
||||||
assigned_topic = Fabricate(:post).topic
|
assigned_topic = Fabricate(:post).topic
|
||||||
unassigned_topic = Fabricate(:topic)
|
Fabricate(:topic)
|
||||||
|
|
||||||
TopicAssigner.new(assigned_topic, user).assign(user)
|
TopicAssigner.new(assigned_topic, user).assign(user)
|
||||||
query = TopicQuery.new(user, assigned: "*").list_latest
|
query = TopicQuery.new(user, assigned: "*").list_latest
|
||||||
|
@ -188,10 +205,10 @@ describe TopicQuery do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def assign_to(topic, user)
|
def assign_to(topic, user, assignee)
|
||||||
topic.tap do |t|
|
topic.tap do |t|
|
||||||
t.posts << Fabricate(:post)
|
t.posts << Fabricate(:post)
|
||||||
TopicAssigner.new(t, user).assign(user)
|
TopicAssigner.new(t, user).assign(assignee)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -19,6 +19,7 @@ acceptance("Discourse Assign | Assign mobile", function (needs) {
|
||||||
return helper.response({
|
return helper.response({
|
||||||
success: true,
|
success: true,
|
||||||
assign_allowed_groups: false,
|
assign_allowed_groups: false,
|
||||||
|
assign_allowed_for_groups: [],
|
||||||
suggestions: [
|
suggestions: [
|
||||||
{
|
{
|
||||||
id: 19,
|
id: 19,
|
||||||
|
@ -60,6 +61,7 @@ acceptance("Discourse Assign | Assign desktop", function (needs) {
|
||||||
return helper.response({
|
return helper.response({
|
||||||
success: true,
|
success: true,
|
||||||
assign_allowed_groups: false,
|
assign_allowed_groups: false,
|
||||||
|
assign_allowed_for_groups: [],
|
||||||
suggestions: [
|
suggestions: [
|
||||||
{
|
{
|
||||||
id: 19,
|
id: 19,
|
||||||
|
|
|
@ -10,7 +10,7 @@ discourseModule(
|
||||||
setupRenderingTest(hooks);
|
setupRenderingTest(hooks);
|
||||||
|
|
||||||
componentTest("displays username and name", {
|
componentTest("displays username and name", {
|
||||||
template: hbs`{{group-assigned-filter show-avatar=true filter=filter}}`,
|
template: hbs`{{group-assigned-filter showAvatar=true filter=filter}}`,
|
||||||
|
|
||||||
beforeEach() {
|
beforeEach() {
|
||||||
this.set("filter", {
|
this.set("filter", {
|
||||||
|
|
Loading…
Reference in New Issue