From c67987f49a30fc028840989e0757ed6ab162f6f2 Mon Sep 17 00:00:00 2001 From: Jennifer Taylor Date: Sun, 30 Aug 2026 17:02:32 +0000 Subject: [PATCH] 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. --- bemani/data/mysql/user.py | 3 ++- bemani/data/types.py | 14 +++++++++-- bemani/frontend/admin/admin.py | 1 + .../static/controllers/admin/cards.react.js | 9 ++++--- .../static/controllers/admin/users.react.js | 25 ++++++++++++++++--- 5 files changed, 42 insertions(+), 10 deletions(-) diff --git a/bemani/data/mysql/user.py b/bemani/data/mysql/user.py index 6d82916..b4af3fc 100644 --- a/bemani/data/mysql/user.py +++ b/bemani/data/mysql/user.py @@ -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() ] diff --git a/bemani/data/types.py b/bemani/data/types.py index 821eb83..83f3e86 100644 --- a/bemani/data/types.py +++ b/bemani/data/types.py @@ -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: diff --git a/bemani/frontend/admin/admin.py b/bemani/frontend/admin/admin.py index 6d947d0..3b7f123 100644 --- a/bemani/frontend/admin/admin.py +++ b/bemani/frontend/admin/admin.py @@ -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, } diff --git a/bemani/frontend/static/controllers/admin/cards.react.js b/bemani/frontend/static/controllers/admin/cards.react.js index 16d0ae4..b836104 100644 --- a/bemani/frontend/static/controllers/admin/cards.react.js +++ b/bemani/frontend/static/controllers/admin/cards.react.js @@ -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 ( <> - + ); }, diff --git a/bemani/frontend/static/controllers/admin/users.react.js b/bemani/frontend/static/controllers/admin/users.react.js index 1b5f5dc..9340c0d 100644 --- a/bemani/frontend/static/controllers/admin/users.react.js +++ b/bemani/frontend/static/controllers/admin/users.react.js @@ -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 ? + {user.linked_cards} : + none; + }, + + 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 (