Clarify card delete option on admin cards page, add number of linked cards to admin user search page, make anonymous accounts sort last in ascending sort order on admin user search page.

This commit is contained in:
Jennifer Taylor
2026-08-30 17:02:32 +00:00
parent e16d0ff659
commit c67987f49a
5 changed files with 42 additions and 10 deletions

View File

@@ -330,7 +330,7 @@ class UserData(BaseData):
Returns:
A list of User objects representing all users.
"""
sql = "SELECT id, username, email, admin FROM user"
sql = "SELECT id, username, email, admin, (SELECT COUNT(id) FROM card WHERE card.userid = user.id) AS linked_cards FROM user"
cursor = self.execute(sql)
return [
User(
@@ -338,6 +338,7 @@ class UserData(BaseData):
result["username"],
result["email"],
result["admin"] == 1,
result["linked_cards"],
)
for result in cursor.mappings()
]

View File

@@ -15,7 +15,14 @@ class User:
more cards, or swap out a card for a new one.
"""
def __init__(self, userid: UserID, username: Optional[str], email: Optional[str], admin: bool) -> None:
def __init__(
self,
userid: UserID,
username: Optional[str],
email: Optional[str],
admin: bool,
linked_cards: Optional[int] = None,
) -> None:
"""
Initialize the user object.
@@ -25,14 +32,17 @@ class User:
the web UI.
email - An optional string, set if the user has claimed their account on the
web UI.
admin - Whether this user is an admin or not.
linked_cards - The count of cards linked to this account. Not always provided.
"""
self.id = userid
self.username = username
self.email = email
self.admin = admin
self.linked_cards = linked_cards
def __repr__(self) -> str:
return f"User(userid={self.id}, username={self.username}, email={self.email}, admin={self.admin})"
return f"User(userid={self.id}, username={self.username}, email={self.email}, admin={self.admin}, linked_cards={self.linked_cards})"
class Achievement:

View File

@@ -99,6 +99,7 @@ def format_user(user: User) -> Dict[str, Any]:
"username": user.username,
"email": user.email,
"admin": user.admin,
"linked_cards": user.linked_cards,
}

View File

@@ -39,7 +39,10 @@ var card_management = createReactClass({
animation: 'none',
closeAnimation: 'none',
title: 'Delete Card',
content: 'Are you sure you want to delete this card?',
content: (
'Are you sure you want to delete this card? Doing so will remove the card from the ' +
'user account but will not delete the account itself.'
),
buttons: {
Delete: {
btnClass: 'delete',
@@ -93,12 +96,12 @@ var card_management = createReactClass({
return (
<>
<Edit
title="view/edit"
title="view/edit user"
onClick={function(event) {
window.location=Link.get('viewuser', card.id);
}.bind(this)}
/>
<Delete onClick={this.deleteExistingCard.bind(this, card.number)} />
<Delete title="remove card" onClick={this.deleteExistingCard.bind(this, card.number)} />
</>
);
},

View File

@@ -41,8 +41,8 @@ var card_management = createReactClass({
},
sortUsername: function(a, b) {
var au = a.username ? a.username : '';
var bu = b.username ? b.username : '';
var au = a.username ? a.username : '\u10FFFF';
var bu = b.username ? b.username : '\u10FFFF';
return au.localeCompare(bu);
},
@@ -53,11 +53,23 @@ var card_management = createReactClass({
},
sortEmail: function(a, b) {
var ae = a.email ? a.email : '';
var be = b.email ? b.email : '';
var ae = a.email ? a.email : '\u10FFFF';
var be = b.email ? b.email : '\u10FFFF';
return ae.localeCompare(be);
},
renderLinkedCards: function(user) {
return user.linked_cards ?
<span>{user.linked_cards}</span> :
<span className="placeholder">none</span>;
},
sortLinkedCards: function(a, b) {
var ac = a.linked_cards ? a.linked_cards : 0;
var bc = b.linked_cards ? b.linked_cards : 0;
return ac - bc;
},
renderEditButton: function(user) {
return (
<Nav
@@ -115,6 +127,11 @@ var card_management = createReactClass({
render: this.renderEmail,
sort: this.sortEmail,
},
{
name: 'Linked Cards',
render: this.renderLinkedCards,
sort: this.sortLinkedCards,
},
{
name: '',
action: true,