last activity for players
This commit is contained in:
@@ -141,8 +141,6 @@ const PlayerListItem = memo(({ stat, onPlayerClick, mmr }: PlayerListItemProps)
|
||||
);
|
||||
});
|
||||
|
||||
PlayerListItem.displayName = 'PlayerListItem';
|
||||
|
||||
const PlayerStatsTable = () => {
|
||||
const { data: playerStats } = useAllPlayerStats();
|
||||
const navigate = useNavigate();
|
||||
|
||||
118
src/features/players/components/players-activity-table.tsx
Normal file
118
src/features/players/components/players-activity-table.tsx
Normal file
@@ -0,0 +1,118 @@
|
||||
import { memo } from "react";
|
||||
import {
|
||||
Text,
|
||||
Stack,
|
||||
Group,
|
||||
Box,
|
||||
Container,
|
||||
Divider,
|
||||
UnstyledButton,
|
||||
} from "@mantine/core";
|
||||
import { Player } from "../types";
|
||||
import { usePlayersActivity } from "../queries";
|
||||
|
||||
interface PlayerActivityItemProps {
|
||||
player: Player;
|
||||
}
|
||||
|
||||
const PlayerActivityItem = memo(({ player }: PlayerActivityItemProps) => {
|
||||
const playerName = player.first_name && player.last_name
|
||||
? `${player.first_name} ${player.last_name}`
|
||||
: player.first_name || player.last_name || "Unknown Player";
|
||||
|
||||
const formatDate = (dateStr?: string) => {
|
||||
if (!dateStr) return "Never";
|
||||
const date = new Date(dateStr);
|
||||
return date.toLocaleString();
|
||||
};
|
||||
|
||||
const getTimeSince = (dateStr?: string) => {
|
||||
if (!dateStr) return "Never active";
|
||||
const date = new Date(dateStr);
|
||||
const now = new Date();
|
||||
const diffMs = now.getTime() - date.getTime();
|
||||
const diffMins = Math.floor(diffMs / 60000);
|
||||
const diffHours = Math.floor(diffMins / 60);
|
||||
const diffDays = Math.floor(diffHours / 24);
|
||||
|
||||
if (diffMins < 1) return "Just now";
|
||||
if (diffMins < 60) return `${diffMins}m ago`;
|
||||
if (diffHours < 24) return `${diffHours}h ago`;
|
||||
if (diffDays < 30) return `${diffDays}d ago`;
|
||||
return formatDate(dateStr);
|
||||
};
|
||||
|
||||
const isActive = player.last_activity &&
|
||||
(new Date().getTime() - new Date(player.last_activity).getTime()) < 5 * 60 * 1000;
|
||||
|
||||
return (
|
||||
<Box
|
||||
w="100%"
|
||||
p="md"
|
||||
style={{
|
||||
borderRadius: 0,
|
||||
}}
|
||||
>
|
||||
<Group justify="space-between" align="flex-start" w="100%">
|
||||
<Stack gap={4} flex={1}>
|
||||
<Group gap="xs">
|
||||
<Text size="sm" fw={600}>
|
||||
{playerName}
|
||||
</Text>
|
||||
{isActive && (
|
||||
<Box
|
||||
w={8}
|
||||
h={8}
|
||||
style={{
|
||||
borderRadius: "50%",
|
||||
backgroundColor: "var(--mantine-color-green-6)",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Group>
|
||||
<Group gap="md">
|
||||
<Text size="xs" c="dimmed">
|
||||
{getTimeSince(player.last_activity)}
|
||||
</Text>
|
||||
{player.last_activity && (
|
||||
<Text size="xs" c="dimmed">
|
||||
{formatDate(player.last_activity)}
|
||||
</Text>
|
||||
)}
|
||||
</Group>
|
||||
</Stack>
|
||||
</Group>
|
||||
</Box>
|
||||
);
|
||||
});
|
||||
|
||||
export const PlayersActivityTable = () => {
|
||||
const { data: players } = usePlayersActivity();
|
||||
|
||||
return (
|
||||
<Container size="100%" px={0}>
|
||||
<Stack gap="xs">
|
||||
<Group px="md" justify="space-between" align="center">
|
||||
<Text size="10px" lh={0} c="dimmed">
|
||||
{players.length} players
|
||||
</Text>
|
||||
</Group>
|
||||
|
||||
<Stack gap={0}>
|
||||
{players.map((player: Player, index: number) => (
|
||||
<Box key={player.id}>
|
||||
<PlayerActivityItem player={player} />
|
||||
{index < players.length - 1 && <Divider />}
|
||||
</Box>
|
||||
))}
|
||||
</Stack>
|
||||
|
||||
{players.length === 0 && (
|
||||
<Text ta="center" c="dimmed" py="xl">
|
||||
No player activity found
|
||||
</Text>
|
||||
)}
|
||||
</Stack>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
@@ -1,3 +1,5 @@
|
||||
import { Logger } from "@/lib/logger";
|
||||
|
||||
export const logger = new Logger('Players');
|
||||
export const logger = new Logger('Players');
|
||||
export * from "./queries";
|
||||
export { PlayersActivityTable } from "./components/players-activity-table";
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useServerSuspenseQuery } from "@/lib/tanstack-query/hooks";
|
||||
import { listPlayers, getPlayer, getUnassociatedPlayers, fetchMe, getPlayerStats, getAllPlayerStats, getPlayerMatches, getUnenrolledPlayers } from "./server";
|
||||
import { listPlayers, getPlayer, getUnassociatedPlayers, fetchMe, getPlayerStats, getAllPlayerStats, getPlayerMatches, getUnenrolledPlayers, getPlayersActivity } from "./server";
|
||||
|
||||
export const playerKeys = {
|
||||
auth: ['auth'],
|
||||
@@ -10,6 +10,7 @@ export const playerKeys = {
|
||||
stats: (id: string) => ['players', 'stats', id],
|
||||
allStats: ['players', 'stats', 'all'],
|
||||
matches: (id: string) => ['players', 'matches', id],
|
||||
activity: ['players', 'activity'],
|
||||
};
|
||||
|
||||
export const playerQueries = {
|
||||
@@ -45,6 +46,10 @@ export const playerQueries = {
|
||||
queryKey: playerKeys.matches(id),
|
||||
queryFn: async () => await getPlayerMatches({ data: id })
|
||||
}),
|
||||
activity: () => ({
|
||||
queryKey: playerKeys.activity,
|
||||
queryFn: async () => await getPlayersActivity()
|
||||
}),
|
||||
};
|
||||
|
||||
export const useMe = () => {
|
||||
@@ -89,4 +94,7 @@ export const usePlayerMatches = (id: string) =>
|
||||
useServerSuspenseQuery(playerQueries.matches(id));
|
||||
|
||||
export const useUnenrolledPlayers = (tournamentId: string) =>
|
||||
useServerSuspenseQuery(playerQueries.unenrolled(tournamentId));
|
||||
useServerSuspenseQuery(playerQueries.unenrolled(tournamentId));
|
||||
|
||||
export const usePlayersActivity = () =>
|
||||
useServerSuspenseQuery(playerQueries.activity());
|
||||
@@ -161,3 +161,9 @@ export const getUnenrolledPlayers = createServerFn()
|
||||
.handler(async ({ data: tournamentId }) =>
|
||||
toServerResult(async () => await pbAdmin.getUnenrolledPlayers(tournamentId))
|
||||
);
|
||||
|
||||
export const getPlayersActivity = createServerFn()
|
||||
.middleware([superTokensFunctionMiddleware])
|
||||
.handler(async () =>
|
||||
toServerResult<Player[]>(async () => await pbAdmin.getPlayersActivity())
|
||||
);
|
||||
|
||||
@@ -14,6 +14,7 @@ export interface Player {
|
||||
last_name?: string;
|
||||
created?: string;
|
||||
updated?: string;
|
||||
last_activity?: string;
|
||||
teams?: TeamInfo[];
|
||||
}
|
||||
|
||||
@@ -23,7 +24,9 @@ export const playerInputSchema = z.object({
|
||||
last_name: z.string().min(2).max(20).regex(/^[a-zA-Z0-9\s]+$/, "Last name must be 2-20 characters long and contain only letters and spaces"),
|
||||
});
|
||||
|
||||
export const playerUpdateSchema = playerInputSchema.partial();
|
||||
export const playerUpdateSchema = playerInputSchema.extend({
|
||||
last_activity: z.string().optional(),
|
||||
}).partial();
|
||||
|
||||
export type PlayerInput = z.infer<typeof playerInputSchema>;
|
||||
export type PlayerUpdateInput = z.infer<typeof playerUpdateSchema>;
|
||||
|
||||
Reference in New Issue
Block a user