Compare commits

...

31 Commits

Author SHA1 Message Date
caf19de2b1 experimenting 2025-03-27 20:29:01 -07:00
bb033ad81f Filter user cards based on user_field.3 2025-03-27 20:28:12 -07:00
Jarek Radosz
b803d64e51
DEV: Update linting (#52) 2025-03-19 11:59:08 +00:00
David Taylor
5f12e99dd2
DEV: Upgrade modifyClass syntax to remove object-literal decorators (#50)
Decorators on object literal properties are unsupported in most modern JS tooling, including ts/glint and Prettier 3.0. Using the new native-class-based modifyClass syntax means we can use decorators safely
2025-03-05 10:36:25 +00:00
Jarek Radosz
63f9de0119
DEV: Update license (#48) 2025-02-24 11:33:42 +08:00
dependabot[bot]
b4d9701e36
bump rack from 3.1.8 to 3.1.10 (#47)
---
updated-dependencies:
- dependency-name: rack
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-12 22:20:56 +01:00
David Taylor
7519cbc614
DEV: Bump dependencies and fix linting (#46) 2025-02-06 19:56:28 +01:00
David Taylor
2f939cb949
DEV: Pin version for Discourse <3.5.0.beta1-dev (#45) 2025-02-05 19:50:57 +01:00
Kelv
72af869d92
DEV: Update deprecated Font Awesome icon names (#44) 2024-12-06 20:25:54 +01:00
David Taylor
533c97fc4f
DEV: Update linting (#43) 2024-11-28 15:46:28 +01:00
David Taylor
63d6efc69f
DEV: Update linting (#42) 2024-11-20 19:30:13 +01:00
David Taylor
b67ceac39d
DEV: Update eslint config (#41) 2024-11-19 15:37:33 +01:00
Kelv
f2b51b4b22
DEV: Update linting setup (#39) 2024-10-17 11:34:10 +02:00
Natalie Tay
010ada35b7
FIX: Update order when column is clicked (#40)
In #29209, we introduced a callback to the TableHeaderToggle's parent to update the column order. This commit is to update this component to use that callback.
2024-10-17 00:15:53 +08:00
Kelv
8f569de21b
DEV: Switch to use pnpm (#38) 2024-10-14 11:31:27 +02:00
Penar Musaraj
e635539583
DEV: update core extension of UserCardContents (#37)
Should fix tests, they were failing with this error:

Attempted to register a view with an id already in use: user-card...

Co-authored-by: Sérgio Saquetim <1108771+megothss@users.noreply.github.com>
2024-10-07 15:05:36 -04:00
dependabot[bot]
8b65992b25
Bump micromatch from 4.0.5 to 4.0.8 (#35)
Bumps [micromatch](https://github.com/micromatch/micromatch) from 4.0.5 to 4.0.8.
- [Release notes](https://github.com/micromatch/micromatch/releases)
- [Changelog](https://github.com/micromatch/micromatch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/micromatch/compare/4.0.5...4.0.8)

---
updated-dependencies:
- dependency-name: micromatch
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-01 12:42:53 +02:00
Discourse Translator Bot
7f5163cf75
Update translations (#34) 2024-08-20 17:55:57 +02:00
Discourse Translator Bot
6ddd3b7a7c
Update translations (#33) 2024-08-14 08:12:15 +02:00
Discourse Translator Bot
6b4ae1dc90
Update translations (#32) 2024-08-13 16:35:47 +02:00
Natalie Tay
2474115525
DEV: Pin theme for Discourse < 3.4.0.beta1-dev (#31) 2024-08-02 17:44:44 +08:00
Discourse Translator Bot
195e853216
Update translations (#30) 2024-06-25 15:41:33 +02:00
dependabot[bot]
8d0d31ee4c
Bump braces from 3.0.2 to 3.0.3 (#29)
Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3.
- [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3)

---
updated-dependencies:
- dependency-name: braces
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-20 11:32:35 +02:00
Jarek Radosz
0e769465cf
DEV: Update the template copied from core (#28)
(and use angle brackets in a connector template)
2024-06-19 14:42:05 +02:00
Jarek Radosz
36dd79a8c3
FIX: Regression after a core change (#27)
a3d0a9edbb in core converted the Users route to a native class, which meant that `queryParams` merging no longer works.

Extend it manually in `init()`.
2024-06-19 14:12:21 +02:00
Discourse Translator Bot
1ef7e3e2b1
Update translations (#26) 2024-06-18 15:42:03 +02:00
Discourse Translator Bot
c5c9a81d93
Update translations (#25) 2024-06-11 17:23:09 +02:00
Discourse Translator Bot
1eca58f9f1
DEV: Add Crowdin support (#24) 2024-06-11 13:38:24 +02:00
JimmyJammyDodger
cdb117a1a7
Update about.json (#23) 2024-06-10 09:37:55 +02:00
David Taylor
1902277ff0
DEV: Update linting (#22)
* drop jQ

* qunit-dom

* convert the button to gjs

* use the store

---------

Co-authored-by: Jarek Radosz <jradosz@gmail.com>
2024-03-28 10:12:48 +00:00
Selase Krakani
d0c50fcbaa
DEV: Pin theme for Discourse < 3.3.0.beta1-dev (#21) 2024-02-29 13:03:32 -07:00
76 changed files with 4865 additions and 3308 deletions

View File

@ -1,2 +1,6 @@
< 3.5.0.beta1-dev: 72af869d92500fd4971817fc31a57593416ef832
< 3.4.0.beta2-dev: 533c97fc4fbf65310bdd7d3d13eebefddc4d5ffd
< 3.4.0.beta1-dev: 195e853216558cdc16db598656619aa08abf8927
< 3.3.0.beta1-dev: 4d7491226a4eac33f5a57ebd67f292ef91c8c51e
3.1.999: 6942afd56f4cf43e643bbafe1782aa055fadc7bc 3.1.999: 6942afd56f4cf43e643bbafe1782aa055fadc7bc
2.8.0.beta1: d9ed154f9887dcf48e648d7fa8af76c65caa5006 2.8.0.beta1: d9ed154f9887dcf48e648d7fa8af76c65caa5006

View File

@ -1,8 +0,0 @@
{
"extends": "eslint-config-discourse",
"ignorePatterns": ["javascripts/vendor/*"],
"globals": {
"settings": "readonly",
"themePrefix": "readonly"
}
}

2
.npmrc Normal file
View File

@ -0,0 +1,2 @@
engine-strict = true
auto-install-peers = false

View File

@ -1 +0,0 @@
{}

1
.prettierrc.cjs Normal file
View File

@ -0,0 +1 @@
module.exports = require("@discourse/lint-configs/prettier");

2
.rubocop.yml Normal file
View File

@ -0,0 +1,2 @@
inherit_gem:
rubocop-discourse: stree-compat.yml

2
.streerc Normal file
View File

@ -0,0 +1,2 @@
--print-width=100
--plugins=plugin/trailing_comma,plugin/disable_auto_ternary

1
.template-lintrc.cjs Normal file
View File

@ -0,0 +1 @@
module.exports = require("@discourse/lint-configs/template-lint");

View File

@ -1,4 +0,0 @@
module.exports = {
plugins: ["ember-template-lint-plugin-discourse"],
extends: "discourse:recommended",
};

8
Gemfile Normal file
View File

@ -0,0 +1,8 @@
# frozen_string_literal: true
source "https://rubygems.org"
group :development do
gem "rubocop-discourse"
gem "syntax_tree"
end

101
Gemfile.lock Normal file
View File

@ -0,0 +1,101 @@
GEM
remote: https://rubygems.org/
specs:
activesupport (8.0.2)
base64
benchmark (>= 0.3)
bigdecimal
concurrent-ruby (~> 1.0, >= 1.3.1)
connection_pool (>= 2.2.5)
drb
i18n (>= 1.6, < 2)
logger (>= 1.4.2)
minitest (>= 5.1)
securerandom (>= 0.3)
tzinfo (~> 2.0, >= 2.0.5)
uri (>= 0.13.1)
ast (2.4.2)
base64 (0.2.0)
benchmark (0.4.0)
bigdecimal (3.1.9)
concurrent-ruby (1.3.5)
connection_pool (2.5.0)
drb (2.2.1)
i18n (1.14.7)
concurrent-ruby (~> 1.0)
json (2.10.2)
language_server-protocol (3.17.0.4)
lint_roller (1.1.0)
logger (1.6.6)
minitest (5.25.5)
parallel (1.26.3)
parser (3.3.7.1)
ast (~> 2.4.1)
racc
prettier_print (1.2.1)
racc (1.8.1)
rack (3.1.12)
rainbow (3.1.1)
regexp_parser (2.10.0)
rubocop (1.74.0)
json (~> 2.3)
language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.1.0)
parallel (~> 1.10)
parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 2.9.3, < 3.0)
rubocop-ast (>= 1.38.0, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 4.0)
rubocop-ast (1.39.0)
parser (>= 3.3.1.0)
rubocop-capybara (2.22.1)
lint_roller (~> 1.1)
rubocop (~> 1.72, >= 1.72.1)
rubocop-discourse (3.12.1)
activesupport (>= 6.1)
lint_roller (>= 1.1.0)
rubocop (>= 1.73.2)
rubocop-capybara (>= 2.22.0)
rubocop-factory_bot (>= 2.27.0)
rubocop-rails (>= 2.30.3)
rubocop-rspec (>= 3.0.1)
rubocop-rspec_rails (>= 2.31.0)
rubocop-factory_bot (2.27.1)
lint_roller (~> 1.1)
rubocop (~> 1.72, >= 1.72.1)
rubocop-rails (2.30.3)
activesupport (>= 4.2.0)
lint_roller (~> 1.1)
rack (>= 1.1)
rubocop (>= 1.72.1, < 2.0)
rubocop-ast (>= 1.38.0, < 2.0)
rubocop-rspec (3.5.0)
lint_roller (~> 1.1)
rubocop (~> 1.72, >= 1.72.1)
rubocop-rspec_rails (2.31.0)
lint_roller (~> 1.1)
rubocop (~> 1.72, >= 1.72.1)
rubocop-rspec (~> 3.5)
ruby-progressbar (1.13.0)
securerandom (0.4.1)
syntax_tree (6.2.0)
prettier_print (>= 1.2.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
unicode-display_width (3.1.4)
unicode-emoji (~> 4.0, >= 4.0.4)
unicode-emoji (4.0.4)
uri (1.0.3)
PLATFORMS
arm64-darwin-23
ruby
DEPENDENCIES
rubocop-discourse
syntax_tree
BUNDLED WITH
2.6.6

View File

@ -1,6 +1,6 @@
MIT License The MIT License (MIT)
Copyright (c) 2022 Discourse Copyright (c) Civilized Discourse Construction Kit, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -2,12 +2,15 @@
"name": "User Card Directory", "name": "User Card Directory",
"component": true, "component": true,
"about_url": "https://meta.discourse.org/t/user-card-directory/144479", "about_url": "https://meta.discourse.org/t/user-card-directory/144479",
"license_url": null, "license_url": "https://github.com/discourse/discourse-user-card-directory/blob/main/LICENSE",
"authors": "David Taylor", "authors": "David Taylor",
"minimum_discourse_version": "2.5.0.beta2", "minimum_discourse_version": "2.5.0.beta2",
"assets": {}, "assets": {},
"color_schemes": {}, "color_schemes": {},
"modifiers": { "modifiers": {
"svg_icons": ["id-card", "th-list"] "svg_icons": [
"id-card",
"table-list"
]
} }
} }

View File

@ -1,3 +1,23 @@
.public-user-field__display-in-directory {
display: none !important;
}
// user profile page
.public-user-field.display-in-directory {
display: none !important;
}
.users-directory {
// hide total number of users row
.total-rows {
display: none !important;
}
}
.toggle-cards-button {
display: none;
}
.directory { .directory {
.filter-name { .filter-name {
float: none; float: none;
@ -20,8 +40,9 @@
.user-card-directory { .user-card-directory {
display: grid; display: grid;
grid-template-columns: 1fr; grid-template-columns: 1fr;
grid-gap: 60px 20px;
margin-top: 60px;
@media only screen and (min-width: 500px) { @media only screen and (min-width: 500px) {
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr)); grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
@ -29,19 +50,12 @@
.user-card-container { .user-card-container {
margin: 60px 20px; margin: 60px 20px;
margin-bottom: auto;
width: 100%;
background-color: var(--secondary);
box-shadow: var(--shadow-card);
} }
@supports (display: grid) {
display: grid;
.user-card-container {
margin: 0;
}
}
grid-gap: 60px 20px;
margin-top: 60px;
.user-card { .user-card {
z-index: z("base"); z-index: z("base");
position: relative; position: relative;
@ -58,14 +72,6 @@
display: none; display: none;
} }
.user-card-container {
width: 100%;
background-color: var(--secondary);
box-shadow: var(--shadow-card);
margin-bottom: auto;
}
.user-card-directory-footer { .user-card-directory-footer {
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(5em, 1fr)); grid-template-columns: repeat(auto-fit, minmax(5em, 1fr));
@ -90,6 +96,7 @@
.label { .label {
font-size: var(--font-down-2); font-size: var(--font-down-2);
.d-icon { .d-icon {
margin-right: 0.2em; margin-right: 0.2em;
color: var(--primary-medium); color: var(--primary-medium);
@ -105,8 +112,10 @@
justify-content: center; justify-content: center;
width: 100%; width: 100%;
min-width: 0; min-width: 0;
span { span {
min-width: 0; min-width: 0;
@include ellipsis; @include ellipsis;
} }
} }

3
eslint.config.mjs Normal file
View File

@ -0,0 +1,3 @@
import DiscourseRecommendedTheme from "@discourse/lint-configs/eslint-theme";
export default [...DiscourseRecommendedTheme];

View File

@ -0,0 +1,32 @@
import Component from "@glimmer/component";
import { action } from "@ember/object";
import { service } from "@ember/service";
import DButton from "discourse/components/d-button";
export default class UserCardDirectoryToggle extends Component {
@service router;
get showingCards() {
const { cards } = this.router.currentRoute.queryParams;
return (
cards === "yes" ||
(cards === undefined && settings.default_view === "cards")
);
}
@action
toggleCards() {
const newValue = this.showingCards ? "no" : "yes";
this.router.transitionTo({ queryParams: { cards: newValue } });
}
<template>
<DButton
@action={{this.toggleCards}}
@icon={{if this.showingCards "table-list" "id-card"}}
@title={{themePrefix (if this.showingCards "show_table" "show_cards")}}
class="btn-default open-edit-columns-btn toggle-cards-button"
/>
</template>
}

View File

@ -1,6 +0,0 @@
{{d-button
icon=(if showingCards "th-list" "id-card")
action=(action "toggleCards")
title=(theme-prefix (if showingCards "show_table" "show_cards"))
class="btn-default open-edit-columns-btn toggle-cards-button"
}}

View File

@ -1,23 +0,0 @@
import Component from "@ember/component";
import { inject as service } from "@ember/service";
import discourseComputed from "discourse-common/utils/decorators";
export default Component.extend({
router: service("router"),
tagName: "",
@discourseComputed("router.currentRoute.queryParams.cards")
showingCards(cardsParam) {
return (
cardsParam === "yes" ||
(cardsParam === undefined && settings.default_view === "cards")
);
},
actions: {
toggleCards() {
const newValue = this.showingCards ? "no" : "yes";
this.router.transitionTo({ queryParams: { cards: newValue } });
},
},
});

View File

@ -1,18 +1,21 @@
import UserCardContents from "discourse/components/user-card-contents";
import { action } from "@ember/object"; import { action } from "@ember/object";
import UserCardContents from "discourse/components/user-card-contents";
export default class UserCardStaticContents extends UserCardContents {
layoutName = "components/user-card-contents";
elementId = null;
export default UserCardContents.extend({
layoutName: "components/user-card-contents",
elementId: null,
// Overriding functions which cause the user card to show/hide based on mouse/keyboard events: // Overriding functions which cause the user card to show/hide based on mouse/keyboard events:
cleanUp() {}, cleanUp() {}
didInsertElement() {},
willDestroyElement() {}, didInsertElement() {}
keyUp() {},
willDestroyElement() {}
keyUp() {}
// need to override this to work with the loading slider // need to override this to work with the loading slider
@action @action
handleShowUser() { handleShowUser() {
return; return;
}, }
}); }

View File

@ -1 +1 @@
{{user-card-directory-toggle}} <UserCardDirectoryToggle />

View File

@ -1,8 +1,7 @@
import { withPluginApi } from "discourse/lib/plugin-api";
import discourseComputed from "discourse-common/utils/decorators";
import User from "discourse/models/user";
import EmberObject, { action } from "@ember/object"; import EmberObject, { action } from "@ember/object";
import { ajax } from "discourse/lib/ajax"; import { ajax } from "discourse/lib/ajax";
import discourseComputed from "discourse/lib/decorators";
import { withPluginApi } from "discourse/lib/plugin-api";
import DiscourseURL, { userPath } from "discourse/lib/url"; import DiscourseURL, { userPath } from "discourse/lib/url";
export default { export default {
@ -12,6 +11,11 @@ export default {
api.modifyClass("route:users", { api.modifyClass("route:users", {
pluginId: "user-card-directory", pluginId: "user-card-directory",
init() {
this._super(...arguments);
this.queryParams.cards = { refreshModel: true };
},
get templateName() { get templateName() {
if (this.modelFor("users")?.showAsCards) { if (this.modelFor("users")?.showAsCards) {
return "users-as-card-directory"; return "users-as-card-directory";
@ -27,10 +31,6 @@ export default {
} }
}, },
queryParams: {
cards: { refreshModel: true },
},
model(params) { model(params) {
return this._super(params).then((model) => { return this._super(params).then((model) => {
model.showAsCards = model.showAsCards =
@ -42,14 +42,14 @@ export default {
}, },
}); });
api.modifyClass("controller:users", { api.modifyClass(
pluginId: "user-card-directory", "controller:users",
cachedUserCardInfo: null, (Superclass) =>
class extends Superclass {
init() { init() {
this.set("cachedUserCardInfo", {}); this.set("cachedUserCardInfo", {});
this._super(...arguments); super.init(...arguments);
}, }
@discourseComputed("model.content.@each") @discourseComputed("model.content.@each")
userCards(allUsers) { userCards(allUsers) {
@ -60,14 +60,15 @@ export default {
if (settings.hide_current_user && this.currentUser) { if (settings.hide_current_user && this.currentUser) {
allUsers = allUsers.filter((u) => u.id !== this.currentUser.id); allUsers = allUsers.filter((u) => u.id !== this.currentUser.id);
} }
const userCardInfos = allUsers.map((u) => { const filteredUsers = allUsers.filter(u => u.user.user_fields["3"] === "true");
const userCardInfos = filteredUsers.map((u) => {
if (this.cachedUserCardInfo[u.id]) { if (this.cachedUserCardInfo[u.id]) {
return this.cachedUserCardInfo[u.id]; return this.cachedUserCardInfo[u.id];
} }
const userCardInfo = (this.cachedUserCardInfo[u.id] = const userCardInfo = (this.cachedUserCardInfo[u.id] =
EmberObject.create({ EmberObject.create({
user: User.create(u.user), user: this.store.createRecord("user", u.user),
directoryItem: u, directoryItem: u,
loading: true, loading: true,
})); }));
@ -82,7 +83,9 @@ export default {
while (toLoad.length > 0) { while (toLoad.length > 0) {
const thisBatch = toLoad.splice(0, loadMax); const thisBatch = toLoad.splice(0, loadMax);
const promise = ajax("/user-cards.json", { const promise = ajax("/user-cards.json", {
data: { user_ids: thisBatch.map((uc) => uc.user.id).join(",") }, data: {
user_ids: thisBatch.map((uc) => uc.user.id).join(","),
},
}); });
thisBatch.forEach((uc) => { thisBatch.forEach((uc) => {
// Each user card expects its own promise // Each user card expects its own promise
@ -101,14 +104,26 @@ export default {
}); });
} }
window.scrollBy(0, 5);
window.scrollBy(0, -5);
return userCardInfos; return userCardInfos;
}, }
@action @action
userCardShowUser(user) { userCardShowUser(user) {
DiscourseURL.routeTo(userPath(user.username_lower)); DiscourseURL.routeTo(userPath(user.username_lower));
}, }
@action
updateOrder(field, asc) {
this.setProperties({
order: field,
asc,
}); });
}
}
);
}); });
}, },
}; };

View File

@ -1,8 +1,13 @@
{{#d-section pageClass="users"}} {{#if this.model.canLoadMore}}
{{#load-more {{hide-application-footer}}
selector=".user-card-directory .user-card-container" {{/if}}
action=(action "loadMore")
}} {{body-class "users-page"}}
<section>
<LoadMore
@selector=".user-card-directory .user-card-container"
@action={{action "loadMore"}}
>
<div class="container"> <div class="container">
<div class="users-directory directory"> <div class="users-directory directory">
<PluginOutlet <PluginOutlet
@ -12,48 +17,45 @@
/> />
<div class="directory-controls"> <div class="directory-controls">
<div class="period-controls"> <div class="period-controls">
{{period-chooser <PeriodChooser
period=period @period={{this.period}}
onChange=(action (mut period)) @onChange={{fn (mut this.period)}}
fullDay=false @fullDay={{false}}
}} />
{{#if lastUpdatedAt}} {{#if this.lastUpdatedAt}}
<div class="directory-last-updated"> <div class="directory-last-updated">
{{i18n "directory.last_updated"}} {{i18n "directory.last_updated"}}
{{lastUpdatedAt}} {{this.lastUpdatedAt}}
</div> </div>
{{/if}} {{/if}}
</div> </div>
<div class="inline-form"> <div class="inline-form">
<label class="total-rows"> <label class="total-rows">
{{#if model.totalRows}} {{#if this.model.totalRows}}
{{i18n "directory.total_rows" count=model.totalRows}} {{i18n "directory.total_rows" count=this.model.totalRows}}
{{/if}} {{/if}}
</label> </label>
<Input <Input
@value={{readonly emailInput}} @value={{readonly this.emailInput}}
placeholder={{I18n "directory.filter_name"}} placeholder={{i18n "directory.filter_name"}}
class="filter-name no-blur" class="filter-name no-blur"
{{on {{on "input" (with-event-value this.onUsernameFilterChanged)}}
"input"
(action "onUsernameFilterChanged" value="target.value")
}}
/> />
{{#if showGroupFilter}} {{#if this.showGroupFilter}}
{{combo-box <ComboBox
@value={{this.group}}
@content={{this.groupOptions}}
@onChange={{this.groupChanged}}
@options={{hash none="directory.group.all"}}
class="directory-group-selector" class="directory-group-selector"
value=group />
content=groupOptions
onChange=(action groupChanged)
options=(hash none="directory.group.all")
}}
{{/if}} {{/if}}
{{#if currentUser.staff}} {{#if this.currentUser.staff}}
{{d-button <DButton
icon="wrench" @icon="wrench"
action=(action "showEditColumnsModal") @action={{this.showEditColumnsModal}}
class="btn-default open-edit-columns-btn" class="btn-default open-edit-columns-btn"
}} />
{{/if}} {{/if}}
<PluginOutlet <PluginOutlet
@name="users-directory-controls" @name="users-directory-controls"
@ -62,21 +64,21 @@
</div> </div>
</div> </div>
{{#conditional-loading-spinner condition=isLoading}} <ConditionalLoadingSpinner @condition={{this.isLoading}}>
{{#if userCards.length}} {{#if this.userCards.length}}
<div class="user-card-directory"> <div class="user-card-directory">
{{#each userCards as |userCard|}} {{#each this.userCards as |userCard|}}
<div class="user-card-container"> <div class="user-card-container">
{{user-card-static <UserCardStatic
user=userCard.user @user={{userCard.user}}
visible=true @visible={{true}}
loading=userCard.loading @loading={{userCard.loading}}
username=userCard.user.username @username={{userCard.user.username}}
showUser=this.userCardShowUser @showUser={{this.userCardShowUser}}
}} />
{{#if (theme-setting "show_stats")}} {{#if (theme-setting "show_stats")}}
<div class="user-card-directory-footer"> <div class="user-card-directory-footer">
{{#each columns as |column|}} {{#each this.columns as |column|}}
<span <span
class="stat stat-{{dasherize column.name}} class="stat stat-{{dasherize column.name}}
stat-type-{{column.type}}" stat-type-{{column.type}}"
@ -97,17 +99,18 @@
{{/if}} {{/if}}
</span> </span>
<span class="label"> <span class="label">
{{table-header-toggle <TableHeaderToggle
field=column.name @onToggle={{this.updateOrder}}
icon=column.icon @field={{column.name}}
order=order @icon={{column.icon}}
asc=asc @order={{this.order}}
automatic=(directory-column-is-automatic @asc={{this.asc}}
@automatic={{directory-column-is-automatic
column=column column=column
)
translated=column.user_field_id
onActiveRender=setActiveHeader
}} }}
@translated={{column.user_field_id}}
@onActiveRender={{this.setActiveHeader}}
/>
</span> </span>
</span> </span>
{{/each}} {{/each}}
@ -116,14 +119,29 @@
</div> </div>
{{/each}} {{/each}}
</div> </div>
{{conditional-loading-spinner condition=model.loadingMore}} <ConditionalLoadingSpinner @condition={{this.model.loadingMore}} />
{{else}} {{else}}
<div class="clearfix"></div> <div class="empty-state">
<p>{{i18n "directory.no_results"}}</p> <div class="empty-state-body">
<p>
{{#if this.name}}
{{i18n "directory.no_results_with_search"}}
{{else}}
{{i18n "directory.no_results.body"}}
{{#if this.currentUser.staff}}
{{html-safe
(i18n
"directory.no_results.extra_body" basePath=(base-path)
)
}}
{{/if}} {{/if}}
{{/conditional-loading-spinner}} {{/if}}
</p>
</div> </div>
</div> </div>
{{/load-more}} {{/if}}
{{/d-section}} </ConditionalLoadingSpinner>
</div>
</div>
</LoadMore>
</section>

14
locales/ar.yml Normal file
View File

@ -0,0 +1,14 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
ar:
theme_metadata:
description: يستبدل دليل المستخدم بشبكة من بطاقات المستخدم
settings:
hide_current_user: إخفاء المستخدم الحالي من الشبكة دائمًا
show_stats: عرض الإحصائيات أسفل كل بطاقة مستخدم
show_cards: العرض في بطاقات
show_table: العرض في جدول

7
locales/be.yml Normal file
View File

@ -0,0 +1,7 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
be:

7
locales/bg.yml Normal file
View File

@ -0,0 +1,7 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
bg:

7
locales/bs_BA.yml Normal file
View File

@ -0,0 +1,7 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
bs_BA:

7
locales/ca.yml Normal file
View File

@ -0,0 +1,7 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
ca:

7
locales/cs.yml Normal file
View File

@ -0,0 +1,7 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
cs:

7
locales/da.yml Normal file
View File

@ -0,0 +1,7 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
da:

14
locales/de.yml Normal file
View File

@ -0,0 +1,14 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
de:
theme_metadata:
description: Ersetzt das Benutzerverzeichnis durch ein Raster aus Benutzerkarten
settings:
hide_current_user: Den aktuellen Benutzer immer aus dem Raster ausblenden
show_stats: Statistiken unter jeder Benutzerkarte anzeigen
show_cards: Als Karten anzeigen
show_table: Als Tabelle anzeigen

7
locales/el.yml Normal file
View File

@ -0,0 +1,7 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
el:

7
locales/en_GB.yml Normal file
View File

@ -0,0 +1,7 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
en_GB:

14
locales/es.yml Normal file
View File

@ -0,0 +1,14 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
es:
theme_metadata:
description: Sustituye el directorio de usuarios por una cuadrícula de tarjetas de usuario
settings:
hide_current_user: Ocultar siempre al usuario actual de la cuadrícula
show_stats: Mostrar estadísticas debajo de cada tarjeta de usuario
show_cards: Mostrar como tarjetas
show_table: Mostrar como tabla

7
locales/et.yml Normal file
View File

@ -0,0 +1,7 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
et:

7
locales/fa_IR.yml Normal file
View File

@ -0,0 +1,7 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
fa_IR:

14
locales/fi.yml Normal file
View File

@ -0,0 +1,14 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
fi:
theme_metadata:
description: Korvaa käyttäjähakemiston käyttäjäkorttien ruudukolla
settings:
hide_current_user: Piilota aina nykyinen käyttäjä ruudukosta
show_stats: Näytä tilastot kunkin käyttäjäkortin alla
show_cards: Näytä kortteina
show_table: Näytä taulukkona

14
locales/fr.yml Normal file
View File

@ -0,0 +1,14 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
fr:
theme_metadata:
description: Remplace le répertoire des utilisateurs par une grille de cartes d'utilisateur
settings:
hide_current_user: Toujours masquer l'utilisateur actuel de la grille
show_stats: Afficher les statistiques sous chaque carte d'utilisateur
show_cards: Afficher sous forme de cartes
show_table: Afficher sous forme de tableau

7
locales/gl.yml Normal file
View File

@ -0,0 +1,7 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
gl:

14
locales/he.yml Normal file
View File

@ -0,0 +1,14 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
he:
theme_metadata:
description: החלפת ספריית המשתמשים ברשת של כרטיסים משתמשים
settings:
hide_current_user: תמיד להסתיר את המשתמש הנוכחי מהרשת
show_stats: הצגת סטטיסטיקה תחת כל כרטיס משתמש
show_cards: הצגה ככרטיסים
show_table: הצגה כטבלה

7
locales/hr.yml Normal file
View File

@ -0,0 +1,7 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
hr:

7
locales/hu.yml Normal file
View File

@ -0,0 +1,7 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
hu:

7
locales/hy.yml Normal file
View File

@ -0,0 +1,7 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
hy:

7
locales/id.yml Normal file
View File

@ -0,0 +1,7 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
id:

14
locales/it.yml Normal file
View File

@ -0,0 +1,14 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
it:
theme_metadata:
description: Sostituisce la directory utente con una griglia di schede utente
settings:
hide_current_user: Nascondi sempre l'utente attuale dalla griglia
show_stats: Mostra le statistiche sotto ogni scheda utente
show_cards: Mostra come schede
show_table: Mostra come tabella

14
locales/ja.yml Normal file
View File

@ -0,0 +1,14 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
ja:
theme_metadata:
description: ユーザーディレクトリをユーザーカードのグリッドに置き換えます
settings:
hide_current_user: 現在のユーザーをグリッドから常に非表示にします
show_stats: 各ユーザーカードの下に統計情報を表示する
show_cards: カードとして表示
show_table: テーブルとして表示

7
locales/ko.yml Normal file
View File

@ -0,0 +1,7 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
ko:

7
locales/lt.yml Normal file
View File

@ -0,0 +1,7 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
lt:

7
locales/lv.yml Normal file
View File

@ -0,0 +1,7 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
lv:

7
locales/nb_NO.yml Normal file
View File

@ -0,0 +1,7 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
nb_NO:

14
locales/nl.yml Normal file
View File

@ -0,0 +1,14 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
nl:
theme_metadata:
description: Vervangt de gebruikerslijst door een raster van gebruikerskaarten
settings:
hide_current_user: Huidige gebruiker altijd verbergen in het raster
show_stats: Statistieken weergeven onder elke gebruikerskaart
show_cards: Weergeven als kaarten
show_table: Weergeven als tabel

7
locales/pl_PL.yml Normal file
View File

@ -0,0 +1,7 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
pl_PL:

7
locales/pt.yml Normal file
View File

@ -0,0 +1,7 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
pt:

14
locales/pt_BR.yml Normal file
View File

@ -0,0 +1,14 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
pt_BR:
theme_metadata:
description: Substitui o diretório do(a) usuário(a) por uma grade com cartões dos(as) usuários(as)
settings:
hide_current_user: Sempre ocultar o(a) usuário(a) da grade
show_stats: Mostrar estatísticas sob o cartão de cada usuário(a)
show_cards: Mostrar como cartões
show_table: Mostrar como tabela

7
locales/ro.yml Normal file
View File

@ -0,0 +1,7 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
ro:

14
locales/ru.yml Normal file
View File

@ -0,0 +1,14 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
ru:
theme_metadata:
description: Заменяет каталог пользователей таблицей карточек пользователей
settings:
hide_current_user: Всегда скрывать текущего пользователя из таблицы
show_stats: Показывать статистику под каждой карточкой пользователя
show_cards: Показывать в виде карточек
show_table: Показывать в виде таблицы

7
locales/sk.yml Normal file
View File

@ -0,0 +1,7 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
sk:

7
locales/sl.yml Normal file
View File

@ -0,0 +1,7 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
sl:

7
locales/sq.yml Normal file
View File

@ -0,0 +1,7 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
sq:

7
locales/sr.yml Normal file
View File

@ -0,0 +1,7 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
sr:

7
locales/sv.yml Normal file
View File

@ -0,0 +1,7 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
sv:

7
locales/sw.yml Normal file
View File

@ -0,0 +1,7 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
sw:

7
locales/te.yml Normal file
View File

@ -0,0 +1,7 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
te:

7
locales/th.yml Normal file
View File

@ -0,0 +1,7 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
th:

14
locales/tr_TR.yml Normal file
View File

@ -0,0 +1,14 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
tr_TR:
theme_metadata:
description: Kullanıcı dizinini kullanıcı kartlarından oluşan bir ızgarayla değiştirir
settings:
hide_current_user: Mevcut kullanıcıyı her zaman ızgaradan gizle
show_stats: Her kullanıcı kartının altında istatistikleri göster
show_cards: Kart olarak göster
show_table: Tablo olarak göster

7
locales/ug.yml Normal file
View File

@ -0,0 +1,7 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
ug:

7
locales/uk.yml Normal file
View File

@ -0,0 +1,7 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
uk:

7
locales/ur.yml Normal file
View File

@ -0,0 +1,7 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
ur:

7
locales/vi.yml Normal file
View File

@ -0,0 +1,7 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
vi:

14
locales/zh_CN.yml Normal file
View File

@ -0,0 +1,14 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
zh_CN:
theme_metadata:
description: 使用用户卡片网格替换用户目录
settings:
hide_current_user: 始终在网格中隐藏当前用户
show_stats: 在每个用户卡片下显示统计信息
show_cards: 以卡片形式显示
show_table: 以表形式显示

7
locales/zh_TW.yml Normal file
View File

@ -0,0 +1,7 @@
# WARNING: Never edit this file.
# It will be overwritten when translations are pulled from Crowdin.
#
# To work with us on translations, join this project:
# https://translate.discourse.org/
zh_TW:

View File

@ -1,10 +1,17 @@
{ {
"name": "discourse-user-card-directory", "private": true,
"version": "1.0.0",
"repository": "https://github.com/discourse/discourse-user-card-directory",
"author": "Discourse",
"license": "MIT",
"devDependencies": { "devDependencies": {
"eslint-config-discourse": "^3.2.0" "@discourse/lint-configs": "2.11.1",
} "ember-template-lint": "7.0.1",
"eslint": "9.22.0",
"prettier": "3.5.3",
"stylelint": "16.16.0"
},
"engines": {
"node": ">= 22",
"npm": "please-use-pnpm",
"yarn": "please-use-pnpm",
"pnpm": "9.x"
},
"packageManager": "pnpm@9.15.5"
} }

4022
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

3
stylelint.config.mjs Normal file
View File

@ -0,0 +1,3 @@
export default {
extends: ["@discourse/lint-configs/stylelint"],
};

View File

@ -1,6 +1,6 @@
import { acceptance, count } from "discourse/tests/helpers/qunit-helpers"; import { click, currentURL, visit } from "@ember/test-helpers";
import { test } from "qunit"; import { test } from "qunit";
import { click, visit } from "@ember/test-helpers"; import { acceptance } from "discourse/tests/helpers/qunit-helpers";
acceptance("User Card Directory", function (needs) { acceptance("User Card Directory", function (needs) {
needs.pretender((server, helper) => { needs.pretender((server, helper) => {
@ -132,33 +132,41 @@ acceptance("User Card Directory", function (needs) {
test("Displays table when cards=no", async function (assert) { test("Displays table when cards=no", async function (assert) {
await visit("/u?cards=no"); await visit("/u?cards=no");
assert.ok($("body.users-page").length, "has the body class"); assert.dom(document.body).hasClass(/users-page/);
assert.equal( assert
count(".directory .directory-table__row"), .dom(".directory .directory-table__row")
2, .exists({ count: 2 }, "has a list of users");
"has a list of users"
);
}); });
test("Displays cards when cards=yes", async function (assert) { test("Displays cards when cards=yes", async function (assert) {
await visit("/u?cards=yes"); await visit("/u?cards=yes");
assert.equal(count(".user-card-avatar"), 2, "has two cards showing"); assert
.dom(".user-card-avatar")
.exists({ count: 2 }, "has two cards showing");
});
test("Updates the order when column is clicked", async function (assert) {
await visit("/u?order=post_count&period=yearly");
await click("#table-header-toggle-post_count");
assert.equal(
currentURL(),
"/u?asc=true&order=post_count&period=yearly",
"sorts by topic count"
);
}); });
test("Can toggle between views", async function (assert) { test("Can toggle between views", async function (assert) {
await visit("/u?cards=no"); await visit("/u?cards=no");
assert.equal( assert
count(".directory .directory-table__row"), .dom(".directory .directory-table__row")
2, .exists({ count: 2 }, "has two table rows");
"has two table rows"
);
await click(".toggle-cards-button"); await click(".toggle-cards-button");
assert.equal(count(".user-card-avatar"), 2, "has two cards"); assert.dom(".user-card-avatar").exists({ count: 2 }, "has two cards");
await click(".toggle-cards-button"); await click(".toggle-cards-button");
assert.equal( assert
count(".directory .directory-table__row"), .dom(".directory .directory-table__row")
2, .exists({ count: 2 }, "has two table rows");
"has two table rows"
);
}); });
}); });

5
translator.yml Normal file
View File

@ -0,0 +1,5 @@
# Configuration file for discourse-translator-bot
files:
- source_path: locales/en.yml
destination_path: translations.yml

3077
yarn.lock

File diff suppressed because it is too large Load Diff