some more styling to the frontend

This commit is contained in:
jenz
2026-08-24 21:52:29 +02:00
parent 28b4e957ea
commit 09107047b9
17 changed files with 275 additions and 147 deletions
@@ -1,7 +1,6 @@
import { NextRequest, NextResponse } from 'next/server';
import { getPlayersPresentDuringMap } from '@/lib/queries';
import { getSteamSummariesBatch, DEFAULT_AVATAR_URL } from '@/lib/steamApi';
import { steam2ToSteam64 } from '@/lib/steam';
import { attachAvatars } from '@/lib/steamApi';
export async function GET(req: NextRequest) {
const mapIdParam = req.nextUrl.searchParams.get('mapId');
@@ -10,21 +9,14 @@ export async function GET(req: NextRequest) {
return NextResponse.json({ error: 'mapId query param is required' }, { status: 400 });
}
const players = await getPlayersPresentDuringMap(mapId);
const rows = await getPlayersPresentDuringMap(mapId);
const enriched = await attachAvatars(rows);
const players = enriched.map((p) => ({
steamid: p.steamid,
name: p.current_name,
country: p.current_country,
avatarUrl: p.avatarUrl,
}));
const steam64ByPlayer = new Map<string, string>();
for (const p of players) {
const id64 = steam2ToSteam64(p.steamid);
if (id64) steam64ByPlayer.set(p.steamid, id64);
}
const avatars = await getSteamSummariesBatch([...steam64ByPlayer.values()]);
const result = players.map((p) => {
const id64 = steam64ByPlayer.get(p.steamid);
const avatarUrl = (id64 && avatars.get(id64)?.avatarUrl) || DEFAULT_AVATAR_URL;
return { steamid: p.steamid, name: p.current_name, avatarUrl };
});
return NextResponse.json({ players: result });
return NextResponse.json({ players });
}
@@ -1,5 +1,6 @@
import { NextRequest, NextResponse } from 'next/server';
import { query } from '@/lib/db';
import { attachAvatars } from '@/lib/steamApi';
interface PlayerRow {
steamid: string;
@@ -7,6 +8,7 @@ interface PlayerRow {
current_country: string | null;
matched_name: string | null;
total_minutes: number | null;
last_connected: string | null;
}
const TOTAL_MINUTES_SUBQUERY = `
@@ -17,6 +19,12 @@ const TOTAL_MINUTES_SUBQUERY = `
LIMIT 1) AS total_minutes
`;
const LAST_CONNECTED_SUBQUERY = `
(SELECT MAX(s.session_end_dt)
FROM playtime_display_sessions s
WHERE s.steamid = p.steamid) AS last_connected
`;
export async function GET(req: NextRequest) {
const q = req.nextUrl.searchParams.get('q')?.trim() ?? '';
const sort = req.nextUrl.searchParams.get('sort') ?? 'recent';
@@ -28,37 +36,41 @@ export async function GET(req: NextRequest) {
? 'p.current_name'
: 'p.last_seen DESC';
// No search term: just list players, ordered per the chosen sort.
let rows: PlayerRow[];
if (!q) {
const rows = await query<PlayerRow>(
// No search term: just list players, ordered per the chosen sort.
rows = await query<PlayerRow>(
`SELECT p.steamid, p.current_name, p.current_country, NULL AS matched_name,
${TOTAL_MINUTES_SUBQUERY}
${TOTAL_MINUTES_SUBQUERY},
${LAST_CONNECTED_SUBQUERY}
FROM playtime_display_players p
ORDER BY ${orderBy}
LIMIT 50`,
);
return NextResponse.json({ players: rows });
} else {
// Search matches steamid directly, or current/previous names.
const like = `%${q}%`;
rows = await query<PlayerRow>(
`SELECT p.steamid, p.current_name, p.current_country, h.player_name AS matched_name,
${TOTAL_MINUTES_SUBQUERY},
${LAST_CONNECTED_SUBQUERY}
FROM playtime_display_players p
LEFT JOIN playtime_display_name_history h
ON h.steamid = p.steamid AND h.player_name LIKE ?
WHERE p.steamid LIKE ?
OR p.current_name LIKE ?
OR EXISTS (
SELECT 1 FROM playtime_display_name_history h2
WHERE h2.steamid = p.steamid AND h2.player_name LIKE ?
)
GROUP BY p.steamid
ORDER BY ${orderBy}
LIMIT 50`,
[like, like, like, like],
);
}
// Search matches steamid directly, or current/previous names.
const like = `%${q}%`;
const rows = await query<PlayerRow>(
`SELECT p.steamid, p.current_name, p.current_country, h.player_name AS matched_name,
${TOTAL_MINUTES_SUBQUERY}
FROM playtime_display_players p
LEFT JOIN playtime_display_name_history h
ON h.steamid = p.steamid AND h.player_name LIKE ?
WHERE p.steamid LIKE ?
OR p.current_name LIKE ?
OR EXISTS (
SELECT 1 FROM playtime_display_name_history h2
WHERE h2.steamid = p.steamid AND h2.player_name LIKE ?
)
GROUP BY p.steamid
ORDER BY ${orderBy}
LIMIT 50`,
[like, like, like, like],
);
return NextResponse.json({ players: rows });
const players = await attachAvatars(rows);
return NextResponse.json({ players });
}
@@ -1,24 +1,16 @@
import { NextResponse } from 'next/server';
import { getCurrentPlayers } from '@/lib/queries';
import { getSteamSummariesBatch, DEFAULT_AVATAR_URL } from '@/lib/steamApi';
import { steam2ToSteam64 } from '@/lib/steam';
import { attachAvatars } from '@/lib/steamApi';
export async function GET() {
const players = await getCurrentPlayers(3);
const rows = await getCurrentPlayers(3);
const enriched = await attachAvatars(rows);
const players = enriched.map((p) => ({
steamid: p.steamid,
name: p.current_name,
country: p.current_country,
avatarUrl: p.avatarUrl,
}));
const steam64ByPlayer = new Map<string, string>();
for (const p of players) {
const id64 = steam2ToSteam64(p.steamid);
if (id64) steam64ByPlayer.set(p.steamid, id64);
}
const avatars = await getSteamSummariesBatch([...steam64ByPlayer.values()]);
const result = players.map((p) => {
const id64 = steam64ByPlayer.get(p.steamid);
const avatarUrl = (id64 && avatars.get(id64)?.avatarUrl) || DEFAULT_AVATAR_URL;
return { steamid: p.steamid, name: p.current_name, avatarUrl };
});
return NextResponse.json({ players: result, count: result.length });
return NextResponse.json({ players, count: players.length });
}
@@ -1,7 +1,6 @@
import { NextRequest, NextResponse } from 'next/server';
import { getPlayersAtTime } from '@/lib/queries';
import { getSteamSummariesBatch, DEFAULT_AVATAR_URL } from '@/lib/steamApi';
import { steam2ToSteam64 } from '@/lib/steam';
import { attachAvatars } from '@/lib/steamApi';
import { formatDbDateTime } from '@/lib/timezone';
export async function GET(req: NextRequest) {
@@ -15,21 +14,14 @@ export async function GET(req: NextRequest) {
return NextResponse.json({ error: 'invalid at timestamp' }, { status: 400 });
}
const players = await getPlayersAtTime(formatDbDateTime(atDate));
const rows = await getPlayersAtTime(formatDbDateTime(atDate));
const enriched = await attachAvatars(rows);
const players = enriched.map((p) => ({
steamid: p.steamid,
name: p.current_name,
country: p.current_country,
avatarUrl: p.avatarUrl,
}));
const steam64ByPlayer = new Map<string, string>();
for (const p of players) {
const id64 = steam2ToSteam64(p.steamid);
if (id64) steam64ByPlayer.set(p.steamid, id64);
}
const avatars = await getSteamSummariesBatch([...steam64ByPlayer.values()]);
const result = players.map((p) => {
const id64 = steam64ByPlayer.get(p.steamid);
const avatarUrl = (id64 && avatars.get(id64)?.avatarUrl) || DEFAULT_AVATAR_URL;
return { steamid: p.steamid, name: p.current_name, avatarUrl };
});
return NextResponse.json({ players: result });
return NextResponse.json({ players });
}
@@ -1,12 +1,12 @@
import { NextRequest, NextResponse } from 'next/server';
import { query } from '@/lib/db';
import { getSteamSummariesBatch, DEFAULT_AVATAR_URL } from '@/lib/steamApi';
import { steam2ToSteam64 } from '@/lib/steam';
import { attachAvatars } from '@/lib/steamApi';
import { parseDbDateTime } from '@/lib/timezone';
interface SessionRow {
steamid: string;
current_name: string;
current_country: string | null;
session_start_dt: string;
session_end_dt: string | null;
}
@@ -28,7 +28,7 @@ export async function GET(req: NextRequest) {
const windowMinutes = Math.round((windowEndMs - windowStartMs) / 60000);
const rows = await query<SessionRow>(
`SELECT s.steamid, COALESCE(p.current_name, s.player_name) AS current_name,
`SELECT s.steamid, COALESCE(p.current_name, s.player_name) AS current_name, p.current_country,
s.session_start_dt, s.session_end_dt
FROM playtime_display_sessions s
LEFT JOIN playtime_display_players p ON p.steamid = s.steamid
@@ -44,7 +44,10 @@ export async function GET(req: NextRequest) {
startMin: number;
endMin: number;
}
const byPlayer = new Map<string, { name: string; segments: Segment[]; totalMinutes: number }>();
const byPlayer = new Map<
string,
{ name: string; country: string | null; segments: Segment[]; totalMinutes: number }
>();
for (const r of rows) {
const sStart = parseDbDateTime(r.session_start_dt).getTime();
@@ -57,33 +60,22 @@ export async function GET(req: NextRequest) {
const endMin = Math.round((clippedEnd - windowStartMs) / 60000);
if (!byPlayer.has(r.steamid)) {
byPlayer.set(r.steamid, { name: r.current_name, segments: [], totalMinutes: 0 });
byPlayer.set(r.steamid, { name: r.current_name, country: r.current_country, segments: [], totalMinutes: 0 });
}
const entry = byPlayer.get(r.steamid)!;
entry.segments.push({ startMin, endMin });
entry.totalMinutes += endMin - startMin;
}
const steam64ByPlayer = new Map<string, string>();
for (const steamid of byPlayer.keys()) {
const id64 = steam2ToSteam64(steamid);
if (id64) steam64ByPlayer.set(steamid, id64);
}
const avatars = await getSteamSummariesBatch([...steam64ByPlayer.values()]);
const unenriched = [...byPlayer.entries()].map(([steamid, data]) => ({
steamid,
name: data.name,
country: data.country,
totalMinutes: data.totalMinutes,
segments: data.segments,
}));
const players = [...byPlayer.entries()]
.map(([steamid, data]) => {
const id64 = steam64ByPlayer.get(steamid);
const avatarUrl = (id64 && avatars.get(id64)?.avatarUrl) || DEFAULT_AVATAR_URL;
return {
steamid,
name: data.name,
avatarUrl,
totalMinutes: data.totalMinutes,
segments: data.segments,
};
})
.sort((a, b) => b.totalMinutes - a.totalMinutes);
const players = (await attachAvatars(unenriched)).sort((a, b) => b.totalMinutes - a.totalMinutes);
return NextResponse.json({ windowMinutes, players });
}