Files
flxn-app/src/features/players/components/players-activity-table.tsx
T
yohlo 0a7b5fa00f
CI/CD Pipeline / Build and Push PocketBase Docker Image (push) Successful in 8s
CI/CD Pipeline / i18n Catalog Check (push) Failing after 10s
CI/CD Pipeline / Build and Push App Docker Image (push) Skipped
CI/CD Pipeline / Deploy to Kubernetes (push) Skipped
i18n
2026-08-08 15:29:40 -07:00

122 lines
3.3 KiB
TypeScript

import { memo } from "react";
import {
Text,
Stack,
Group,
Box,
Container,
Divider,
UnstyledButton,
} from "@mantine/core";
import { Trans, Plural, useLingui } from "@lingui/react/macro";
import { Player } from "../types";
import { usePlayersActivity } from "../queries";
interface PlayerActivityItemProps {
player: Player;
}
const PlayerActivityItem = memo(({ player }: PlayerActivityItemProps) => {
const { t, i18n } = useLingui();
const playerName = player.first_name && player.last_name
? `${player.first_name} ${player.last_name}`
: player.first_name || player.last_name || t`Unknown Player`;
const formatDate = (dateStr?: string) => {
if (!dateStr) return t`Never`;
const date = new Date(dateStr);
return date.toLocaleString(i18n.locale);
};
const getTimeSince = (dateStr?: string) => {
if (!dateStr) return t`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 t`Just now`;
if (diffMins < 60) return t`${diffMins}m ago`;
if (diffHours < 24) return t`${diffHours}h ago`;
if (diffDays < 30) return t`${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">
<Plural value={players.length} one="# player" other="# 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">
<Trans>No player activity found</Trans>
</Text>
)}
</Stack>
</Container>
);
};