admin: show the user last_activity the table is sorted by

The 'Last Activity' column rendered the spawner's last_activity while the
sort handler asks the API for ?sort=last_activity, which orders users by
User.last_activity. The two fields differ, so clicking the sort arrows
reordered rows in a way the column did not explain.

Render User.last_activity in the column; the per-server value is still
shown in the collapsed Server details.
This commit is contained in:
dualfroz committed 2026-09-13 15:16:40 +02:00
1 parent 971e731056
commit 58f26dda7b
2 files changed
+63 -1

No files matched your search

@@ -428,7 +428,9 @@ const ServerDashboard = (props) => {
<p className="text-secondary">{server.name}</p>
</td>
<td data-testid="user-row-last-activity">
{server.last_activity ? timeSince(server.last_activity) : "Never"}
{/* sorted server-side on the user's last_activity; the per-server
value is in the collapsed Server details below */}
{user.last_activity ? timeSince(user.last_activity) : "Never"}
</td>
<td data-testid="user-row-server-start-time">
{server.started ? timeSince(server.started) : "Never"}
@@ -16,6 +16,7 @@ import regeneratorRuntime from "regenerator-runtime";
import ServerDashboard from "./ServerDashboard";
import { initialState, reducers } from "../../Store";
import { timeSince } from "../../util/timeSince";
jest.mock("react-redux", () => ({
...jest.requireActual("react-redux"),
@@ -818,6 +819,65 @@ test("Shows 'Never' for server start time when server never started", async () =
expect(startTimeCell.textContent).toBe("Never");
});
test("Shows the user last activity that the table is sorted by", async () => {
// the column is sorted server-side on User.last_activity, so it has to show
// that field and not the spawner's
const userActivity = new Date(Date.now() - 5 * 60 * 1000).toISOString();
const serverActivity = new Date(
Date.now() - 3 * 60 * 60 * 1000,
).toISOString();
const user = {
...newUser("testuser"),
server: null,
last_activity: userActivity,
servers: {
"": {
name: "",
last_activity: serverActivity,
started: serverActivity,
ready: true,
url: "/user/testuser/",
},
},
};
useSelector.mockImplementation((callback) => {
return callback({
user_data: [user],
user_page: { offset: 0, limit: 2, total: 1 },
limit: 2,
});
});
await act(async () => {
render(serverDashboardJsx());
});
expect(screen.getByTestId("user-row-last-activity").textContent).toBe(
timeSince(userActivity),
);
});
test("Shows 'Never' for last activity when the user was never active", async () => {
const user = { ...newUser("testuser"), server: null, last_activity: null };
useSelector.mockImplementation((callback) => {
return callback({
user_data: [user],
user_page: { offset: 0, limit: 2, total: 1 },
limit: 2,
});
});
await act(async () => {
render(serverDashboardJsx());
});
expect(screen.getByTestId("user-row-last-activity").textContent).toBe(
"Never",
);
});
test("Displays profile information correctly", async () => {
await act(async () => {
render(serverDashboardJsx());