mirror of
https://github.com/discourse/discourse.git
synced 2025-01-19 03:02:46 +08:00
FEATURE: Add links to searchable user fields in users directory and user profile (#29338)
* FEATURE: Add links to searchable user fields in users directory and user profile
This commit is contained in:
parent
13c7773036
commit
708533b1e0
|
@ -0,0 +1,57 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { fn, hash } from "@ember/helper";
|
||||
import { on } from "@ember/modifier";
|
||||
import { action } from "@ember/object";
|
||||
import { LinkTo } from "@ember/routing";
|
||||
import { inject as service } from "@ember/service";
|
||||
|
||||
export default class DirectoryItemUserFieldValueComponent extends Component {
|
||||
@service router;
|
||||
get fieldData() {
|
||||
const { item, column } = this.args;
|
||||
return item?.user?.user_fields?.[column.user_field_id];
|
||||
}
|
||||
|
||||
get values() {
|
||||
const fieldData = this.fieldData;
|
||||
if (!fieldData || !fieldData.value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return fieldData.value
|
||||
.toString()
|
||||
.split(",")
|
||||
.map((v) => v.replace(/-/g, " "))
|
||||
.map((v) => v.trim());
|
||||
}
|
||||
|
||||
get isSearchable() {
|
||||
return this.fieldData?.searchable;
|
||||
}
|
||||
|
||||
@action
|
||||
refreshRoute(value) {
|
||||
this.router.transitionTo({ queryParams: { name: value } });
|
||||
}
|
||||
|
||||
<template>
|
||||
<span class="directory-table__value--user-field">
|
||||
{{#if this.values}}
|
||||
{{#if this.isSearchable}}
|
||||
{{#each this.values as |value|}}
|
||||
<LinkTo
|
||||
@route="users"
|
||||
@query={{hash name=value}}
|
||||
{{on "click" (fn this.refreshRoute value)}}
|
||||
class="directory-value-list-item"
|
||||
>{{value}}</LinkTo>
|
||||
{{/each}}
|
||||
{{else}}
|
||||
{{this.values}}
|
||||
{{/if}}
|
||||
{{else}}
|
||||
-
|
||||
{{/if}}
|
||||
</span>
|
||||
</template>
|
||||
}
|
|
@ -370,7 +370,17 @@
|
|||
<span class="user-field-value">
|
||||
{{#each uf.value as |v|}}
|
||||
{{! some values are arrays }}
|
||||
<span class="user-field-value-list-item">{{v}}</span>
|
||||
<span class="user-field-value-list-item">
|
||||
{{#if uf.field.searchable}}
|
||||
<LinkTo
|
||||
@route="users"
|
||||
@query={{hash name=v}}
|
||||
{{on "click" (fn this.refreshRoute v)}}
|
||||
>{{v}}</LinkTo>
|
||||
{{else}}
|
||||
{{v}}
|
||||
{{/if}}
|
||||
</span>
|
||||
{{else}}
|
||||
{{uf.value}}
|
||||
{{/each}}
|
||||
|
|
|
@ -248,6 +248,11 @@ export default class UserCardContents extends CardContentsBase.extend(
|
|||
this._close();
|
||||
}
|
||||
|
||||
@action
|
||||
refreshRoute(value) {
|
||||
this.router.transitionTo({ queryParams: { name: value } });
|
||||
}
|
||||
|
||||
@action
|
||||
handleShowUser(event) {
|
||||
if (wantsNewWindow(event)) {
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
import { htmlSafe } from "@ember/template";
|
||||
|
||||
export default function directoryItemUserFieldValue(args) {
|
||||
// Args should include key/values { item, column }
|
||||
const value =
|
||||
args.item.user && args.item.user.user_fields
|
||||
? args.item.user.user_fields[args.column.user_field_id]
|
||||
: null;
|
||||
const content = value || "-";
|
||||
return htmlSafe(
|
||||
`<span class='directory-table__value--user-field'>${content}</span>`
|
||||
);
|
||||
}
|
|
@ -260,7 +260,16 @@
|
|||
<span class="user-field-value">
|
||||
{{#each uf.value as |v|}}
|
||||
{{! some values are arrays }}
|
||||
<span class="user-field-value-list-item">{{v}}</span>
|
||||
<span class="user-field-value-list-item">
|
||||
{{#if uf.field.searchable}}
|
||||
<LinkTo
|
||||
@route="users"
|
||||
@query={{hash name=v}}
|
||||
>{{v}}</LinkTo>
|
||||
{{else}}
|
||||
{{v}}
|
||||
{{/if}}
|
||||
</span>
|
||||
{{else}}
|
||||
{{uf.value}}
|
||||
{{/each}}
|
||||
|
|
|
@ -57,6 +57,34 @@ acceptance("User Directory", function () {
|
|||
);
|
||||
});
|
||||
|
||||
test("Searchable user fields display as links", async function (assert) {
|
||||
pretender.get("/directory_items", () => {
|
||||
return response(cloneJSON(directoryFixtures["directory_items"]));
|
||||
});
|
||||
|
||||
await visit("/u");
|
||||
|
||||
const firstRowUserField = query(
|
||||
".directory .directory-table__body .directory-table__row:first-child .directory-table__value--user-field"
|
||||
);
|
||||
|
||||
const userFieldLink = firstRowUserField.querySelector("a");
|
||||
|
||||
assert.ok(userFieldLink, "User field is displayed as a link");
|
||||
|
||||
assert.strictEqual(
|
||||
userFieldLink.getAttribute("href"),
|
||||
"/u?name=Blue&order=likes_received",
|
||||
"The link points to the correct URL"
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
userFieldLink.textContent.trim(),
|
||||
"Blue",
|
||||
"Link text is correct"
|
||||
);
|
||||
});
|
||||
|
||||
test("Visit With Group Filter", async function (assert) {
|
||||
await visit("/u?group=trust_level_0");
|
||||
assert.ok(
|
||||
|
@ -75,7 +103,7 @@ acceptance("User Directory", function () {
|
|||
".directory .directory-table__body .directory-table__row:first-child .directory-table__value--user-field"
|
||||
);
|
||||
|
||||
assert.strictEqual(firstRowUserField.textContent, "Blue");
|
||||
assert.strictEqual(firstRowUserField.textContent.trim(), "Blue");
|
||||
});
|
||||
|
||||
test("Can sort table via keyboard", async function (assert) {
|
||||
|
|
|
@ -14,7 +14,10 @@ export default {
|
|||
post_count: 12263,
|
||||
user: {
|
||||
user_fields: {
|
||||
3: "Blue",
|
||||
3: {
|
||||
value: ["Blue"],
|
||||
searchable: true
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -4,6 +4,11 @@
|
|||
}
|
||||
|
||||
.directory {
|
||||
.directory-value-list-item:not(:empty)
|
||||
~ .directory-value-list-item:not(:empty):before {
|
||||
content: "| ";
|
||||
}
|
||||
|
||||
margin-bottom: 100px;
|
||||
background: var(--d-content-background);
|
||||
|
||||
|
|
|
@ -123,6 +123,9 @@ class DirectoryItemsController < ApplicationController
|
|||
end
|
||||
|
||||
serializer_opts[:attributes] = active_directory_column_names
|
||||
serializer_opts[:searchable_fields] = UserField.where(searchable: true) if serializer_opts[
|
||||
:user_custom_field_map
|
||||
].present?
|
||||
|
||||
serialized = serialize_data(result, DirectoryItemSerializer, serializer_opts)
|
||||
render_json_dump(
|
||||
|
|
|
@ -8,10 +8,25 @@ class DirectoryItemSerializer < ApplicationSerializer
|
|||
|
||||
def user_fields
|
||||
fields = {}
|
||||
user_custom_field_map = @options[:user_custom_field_map] || {}
|
||||
searchable_fields = @options[:searchable_fields] || []
|
||||
|
||||
object.user_custom_fields.each do |cuf|
|
||||
user_field_id = @options[:user_custom_field_map][cuf.name]
|
||||
fields[user_field_id] = cuf.value if user_field_id
|
||||
object.user_custom_fields.each do |custom_field|
|
||||
user_field_id = user_custom_field_map[custom_field.name]
|
||||
next unless user_field_id
|
||||
|
||||
current_value = fields.dig(user_field_id, :value)
|
||||
|
||||
current_value = Array(current_value) if current_value
|
||||
|
||||
new_value = current_value ? current_value << custom_field.value : custom_field.value
|
||||
|
||||
is_searchable = searchable_fields.any? { |field| field.id == user_field_id }
|
||||
|
||||
fields[user_field_id] = {
|
||||
value: new_value.is_a?(Array) ? new_value : [new_value],
|
||||
searchable: is_searchable,
|
||||
}
|
||||
end
|
||||
|
||||
fields
|
||||
|
|
|
@ -248,7 +248,9 @@ RSpec.describe DirectoryItemsController do
|
|||
user_fields.each do |data|
|
||||
user = items[data[:order]]["user"]
|
||||
expect(user["username"]).to eq(data[:user].username)
|
||||
expect(user["user_fields"]).to eq({ data[:field].id.to_s => data[:value] })
|
||||
expect(user["user_fields"]).to eq(
|
||||
{ data[:field].id.to_s => { "searchable" => true, "value" => [data[:value]] } },
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -2,32 +2,96 @@
|
|||
|
||||
RSpec.describe DirectoryItemSerializer do
|
||||
fab!(:user)
|
||||
fab!(:directory_column) do
|
||||
DirectoryColumn.create!(name: "topics_entered", enabled: true, position: 1)
|
||||
end
|
||||
fab!(:user_field_1) { Fabricate(:user_field, name: "user_field_1", searchable: true) }
|
||||
fab!(:user_field_2) { Fabricate(:user_field, name: "user_field_2", searchable: false) }
|
||||
|
||||
before { DirectoryItem.refresh! }
|
||||
|
||||
let :serializer do
|
||||
directory_item =
|
||||
DirectoryItem.find_by(user: user, period_type: DirectoryItem.period_types[:all])
|
||||
DirectoryItemSerializer.new(directory_item, { attributes: DirectoryColumn.active_column_names })
|
||||
context "when serializing user fields" do
|
||||
it "serializes user fields with searchable and non-searchable values" do
|
||||
user.user_custom_fields.create!(name: "user_field_1", value: "Value 1")
|
||||
user.user_custom_fields.create!(name: "user_field_2", value: "Value 2")
|
||||
|
||||
user_fields =
|
||||
serialized_payload(
|
||||
attributes: DirectoryColumn.active_column_names,
|
||||
user_custom_field_map: {
|
||||
"user_field_1" => user_field_1.id,
|
||||
"user_field_2" => user_field_2.id,
|
||||
},
|
||||
searchable_fields: [user_field_1],
|
||||
)
|
||||
|
||||
expect(user_fields).to eq(
|
||||
user_field_1.id => {
|
||||
value: ["Value 1"],
|
||||
searchable: true,
|
||||
},
|
||||
user_field_2.id => {
|
||||
value: ["Value 2"],
|
||||
searchable: false,
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
it "handles multiple values for the same field" do
|
||||
user.user_custom_fields.create!(name: "user_field_1", value: "Value 1")
|
||||
user.user_custom_fields.create!(name: "user_field_1", value: "Another Value")
|
||||
|
||||
user_fields =
|
||||
serialized_payload(
|
||||
attributes: DirectoryColumn.active_column_names,
|
||||
user_custom_field_map: {
|
||||
"user_field_1" => user_field_1.id,
|
||||
},
|
||||
searchable_fields: [],
|
||||
)
|
||||
|
||||
expect(user_fields[user_field_1.id]).to eq(
|
||||
value: ["Value 1", "Another Value"],
|
||||
searchable: false,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
it "Serializes attributes for enabled directory_columns" do
|
||||
DirectoryColumn.update_all(enabled: true)
|
||||
context "when serializing directory columns" do
|
||||
let :serializer do
|
||||
directory_item =
|
||||
DirectoryItem.find_by(user: user, period_type: DirectoryItem.period_types[:all])
|
||||
DirectoryItemSerializer.new(
|
||||
directory_item,
|
||||
{ attributes: DirectoryColumn.active_column_names },
|
||||
)
|
||||
end
|
||||
|
||||
payload = serializer.as_json
|
||||
expect(payload[:directory_item].keys).to include(*DirectoryColumn.pluck(:name).map(&:to_sym))
|
||||
it "serializes attributes for enabled directory_columns" do
|
||||
DirectoryColumn.update_all(enabled: true)
|
||||
|
||||
payload = serializer.as_json
|
||||
expect(payload[:directory_item].keys).to include(*DirectoryColumn.pluck(:name).map(&:to_sym))
|
||||
end
|
||||
|
||||
it "doesn't serialize attributes for disabled directory columns" do
|
||||
DirectoryColumn.update_all(enabled: false)
|
||||
directory_column = DirectoryColumn.first
|
||||
directory_column.update(enabled: true)
|
||||
|
||||
payload = serializer.as_json
|
||||
expect(payload[:directory_item].keys.count).to eq(4)
|
||||
expect(payload[:directory_item]).to have_key(directory_column.name.to_sym)
|
||||
expect(payload[:directory_item]).to have_key(:id)
|
||||
expect(payload[:directory_item]).to have_key(:user)
|
||||
expect(payload[:directory_item]).to have_key(:time_read)
|
||||
end
|
||||
end
|
||||
|
||||
it "Doesn't serialize attributes for disabled directory columns" do
|
||||
DirectoryColumn.update_all(enabled: false)
|
||||
directory_column = DirectoryColumn.first
|
||||
directory_column.update(enabled: true)
|
||||
private
|
||||
|
||||
payload = serializer.as_json
|
||||
expect(payload[:directory_item].keys.count).to eq(4)
|
||||
expect(payload[:directory_item]).to have_key(directory_column.name.to_sym)
|
||||
expect(payload[:directory_item]).to have_key(:id)
|
||||
expect(payload[:directory_item]).to have_key(:user)
|
||||
expect(payload[:directory_item]).to have_key(:time_read)
|
||||
def serialized_payload(serializer_opts)
|
||||
serializer = DirectoryItemSerializer.new(DirectoryItem.find_by(user: user), serializer_opts)
|
||||
serializer.as_json.dig(:directory_item, :user, :user_fields)
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue
Block a user