diff --git a/src/app/routes/_authed/index.tsx b/src/app/routes/_authed/index.tsx
index 853cc4e..aa9c643 100644
--- a/src/app/routes/_authed/index.tsx
+++ b/src/app/routes/_authed/index.tsx
@@ -2,6 +2,7 @@ import { createFileRoute } from "@tanstack/react-router";
import { tournamentQueries, useCurrentTournament } from "@/features/tournaments/queries";
import UpcomingTournament from "@/features/tournaments/components/upcoming-tournament";
import { ensureServerQueryData } from "@/lib/tanstack-query/utils/ensure";
+import StartedTournament from "@/features/tournaments/components/started-tournament";
export const Route = createFileRoute("/_authed/")({
component: Home,
@@ -12,7 +13,7 @@ export const Route = createFileRoute("/_authed/")({
return { tournament }
},
loader: ({ context }) => ({
- withPadding: true,
+ withPadding: false,
header: {
title: context.tournament.name || "FLXN"
}
@@ -22,9 +23,9 @@ export const Route = createFileRoute("/_authed/")({
function Home() {
const { data: tournament } = useCurrentTournament();
- if (!tournament.matches || tournament.matches.length !== 0) {
+ if (!tournament.matches || tournament.matches.length === 0) {
return ;
}
- return
Started Tournament
+ return
}
diff --git a/src/app/routes/api/events.$.ts b/src/app/routes/api/events.$.ts
index b67c086..0481370 100644
--- a/src/app/routes/api/events.$.ts
+++ b/src/app/routes/api/events.$.ts
@@ -9,11 +9,9 @@ export const ServerRoute = createServerFileRoute("/api/events/$").middleware([su
const stream = new ReadableStream({
start(controller) {
- // Send initial connection messages
const connectMessage = `data: ${JSON.stringify({ type: "connected" })}\n\n`;
controller.enqueue(new TextEncoder().encode(connectMessage));
- // Listen for events and broadcast to all connections
const handleEvent = (event: ServerEvent) => {
logger.info('ServerEvents | Event received', event);
const message = `data: ${JSON.stringify(event)}\n\n`;
@@ -25,8 +23,8 @@ export const ServerRoute = createServerFileRoute("/api/events/$").middleware([su
};
serverEvents.on("test", handleEvent);
+ serverEvents.on("match", handleEvent);
- // Keep alive ping every 30 seconds
const pingInterval = setInterval(() => {
try {
const pingMessage = `data: ${JSON.stringify({ type: "ping" })}\n\n`;
diff --git a/src/features/matches/components/match-card.tsx b/src/features/matches/components/match-card.tsx
index 6f1fa3e..5958293 100644
--- a/src/features/matches/components/match-card.tsx
+++ b/src/features/matches/components/match-card.tsx
@@ -29,6 +29,8 @@ const MatchCard = ({ match }: MatchCardProps) => {
}
};
+ console.log(match);
+
return (
{
)}
-
- {match.home?.name!}
-
+
+ {match.home?.name!}
+
-
+
{match.home_cups}
+
+ {match.home?.players.map((p) => (
+
+ {p.first_name} {p.last_name}
+
+ ))}
+
@@ -133,7 +147,7 @@ const MatchCard = ({ match }: MatchCardProps) => {
)}
{
{match.away?.name}
-
+
{match.away_cups}
+
+ {match.away?.players.map((p) => (
+
+ {p.first_name} {p.last_name}
+
+ ))}
+
@@ -163,7 +189,7 @@ const MatchCard = ({ match }: MatchCardProps) => {
border: "1px solid var(--mantine-color-default-border)",
}}
>
-
+
diff --git a/src/features/matches/queries.ts b/src/features/matches/queries.ts
new file mode 100644
index 0000000..7d90237
--- /dev/null
+++ b/src/features/matches/queries.ts
@@ -0,0 +1,18 @@
+import { useServerSuspenseQuery, useServerQuery } from "@/lib/tanstack-query/hooks";
+import { getMatchReactions } from "./server";
+
+export const matchKeys = {
+ list: ['matches', 'list'] as const,
+ details: (id: string) => ['matches', 'details', id] as const,
+ reactions: (id: string) => ['matches', 'reactions', id] as const,
+};
+
+export const matchQueries = {
+ reactions: (matchId: string) => ({
+ queryKey: matchKeys.reactions(matchId),
+ queryFn: () => getMatchReactions({ data: matchId }),
+ }),
+};
+
+export const useMatchReactions = (matchId: string) =>
+ useServerQuery(matchQueries.reactions(matchId));
\ No newline at end of file
diff --git a/src/features/matches/server.ts b/src/features/matches/server.ts
index aa7ed9e..8306f69 100644
--- a/src/features/matches/server.ts
+++ b/src/features/matches/server.ts
@@ -6,6 +6,9 @@ import { z } from "zod";
import { toServerResult } from "@/lib/tanstack-query/utils/to-server-result";
import brackets from "@/features/bracket/utils";
import { MatchInput } from "@/features/matches/types";
+import { serverEvents } from "@/lib/events/emitter";
+import { superTokensFunctionMiddleware } from "@/utils/supertokens";
+import { PlayerInfo } from "../players/types";
const orderedTeamsSchema = z.object({
tournamentId: z.string(),
@@ -150,6 +153,13 @@ export const startMatch = createServerFn()
status: "started",
});
+ console.log('emitting start match...')
+ serverEvents.emit("match", {
+ type: "match",
+ matchId: match.id,
+ tournamentId: match.tournament.id
+ });
+
return match;
})
);
@@ -184,14 +194,9 @@ export const endMatch = createServerFn()
const matchLoser = home_cups < away_cups ? match.home : match.away;
if (!matchWinner || !matchLoser) throw new Error("Something went wrong");
- console.log(matchWinner)
- console.log(matchLoser)
-
// winner -> where to send match winner to, loser same
const { winner, loser } = await pbAdmin.getChildMatches(matchId);
- console.log(winner, loser)
-
// reset match check
if (winner && winner.reset) {
const awayTeamWon = match.away === matchWinner;
@@ -232,8 +237,114 @@ export const endMatch = createServerFn()
});
}
- // TODO: send SSE
+ serverEvents.emit("match", {
+ type: "match",
+ matchId: match.id,
+ tournamentId: match.tournament.id
+ });
return match;
})
);
+
+const toggleReactionSchema = z.object({
+ matchId: z.string(),
+ emoji: z.string(),
+});
+
+export const toggleMatchReaction = createServerFn()
+ .validator(toggleReactionSchema)
+ .middleware([superTokensFunctionMiddleware])
+ .handler(async ({ data: { matchId, emoji }, context }) =>
+ toServerResult(async () => {
+ const user = await pbAdmin.getPlayerByAuthId(context.userAuthId);
+ const userId = user?.id;
+ if (!userId) return;
+
+ const match = await pbAdmin.getMatch(matchId);
+ if (!match) {
+ throw new Error("Match not found");
+ }
+
+ const existingReaction = await pbAdmin.getUserReaction(matchId, userId, emoji);
+ if (existingReaction) {
+ await pbAdmin.deleteReaction(existingReaction.id);
+ logger.info("Removed reaction", { matchId, emoji, userId });
+ } else {
+ await pbAdmin.createReaction(matchId, userId, emoji);
+ logger.info("Added reaction", { matchId, emoji, userId });
+ }
+
+ const all = await pbAdmin.getReactionsForMatch(matchId);
+
+ const reactionsByEmoji = all.reduce((acc, reaction) => {
+ const emoji = reaction.emoji;
+ if (!acc[emoji]) {
+ acc[emoji] = {
+ emoji,
+ count: 0,
+ players: [],
+ };
+ }
+ acc[emoji].count++;
+ acc[emoji].players.push({
+ id: reaction?.player?.id,
+ first_name: reaction.player?.first_name,
+ last_name: reaction.player?.last_name,
+ });
+ return acc;
+ }, {} as Record);
+
+ const reactions = Object.values(reactionsByEmoji);
+
+ serverEvents.emit("reaction", {
+ type: "reaction",
+ matchId,
+ tournamentId: match.tournament.id,
+ reactions,
+ });
+
+ return reactions as Reaction[]
+ })
+ );
+
+export interface Reaction {
+ emoji: string;
+ count: number;
+ players: PlayerInfo[];
+}
+export const getMatchReactions = createServerFn()
+ .validator(z.string())
+ .middleware([superTokensFunctionMiddleware])
+ .handler(async ({ data: matchId, context }) =>
+ toServerResult(async () => {
+ const match = await pbAdmin.getMatch(matchId);
+ if (!match) {
+ throw new Error("Match not found");
+ }
+
+ const all = await pbAdmin.getReactionsForMatch(matchId);
+
+ const reactionsByEmoji = all.reduce((acc, reaction) => {
+ const emoji = reaction.emoji;
+ if (!acc[emoji]) {
+ acc[emoji] = {
+ emoji,
+ count: 0,
+ players: [],
+ };
+ }
+ acc[emoji].count++;
+ acc[emoji].players.push({
+ id: reaction?.player?.id,
+ first_name: reaction.player?.first_name,
+ last_name: reaction.player?.last_name,
+ });
+ return acc;
+ }, {} as Record);
+
+ const reactions = Object.values(reactionsByEmoji);
+
+ return reactions as Reaction[]
+ })
+ );
diff --git a/src/features/players/components/player-list.tsx b/src/features/players/components/player-list.tsx
index 5c2425d..c475c6d 100644
--- a/src/features/players/components/player-list.tsx
+++ b/src/features/players/components/player-list.tsx
@@ -13,7 +13,7 @@ const PlayerList = ({ players, loading = false }: PlayerListProps) => {
const navigate = useNavigate();
const handleClick = useCallback((playerId: string) =>
- navigate({ to: `/profile/${playerId} `}), [navigate]);
+ navigate({ to: `/profile/${playerId}`}), [navigate]);
if (loading) return
{Array.from({ length: 10 }).map((_, i) => (
diff --git a/src/features/reactions/components/emoji-bar.tsx b/src/features/reactions/components/emoji-bar.tsx
index 9bae1ba..6f011c4 100644
--- a/src/features/reactions/components/emoji-bar.tsx
+++ b/src/features/reactions/components/emoji-bar.tsx
@@ -5,43 +5,27 @@ import {
Tabs,
} from "@mantine/core";
import { useDisclosure } from "@mantine/hooks";
-import { useState, useRef } from "react";
+import { useState, useRef, useCallback } from "react";
import Sheet from "@/components/sheet/sheet";
-import { PlayerInfo } from "@/features/players/types";
import PlayerList from "@/features/players/components/player-list";
import EmojiPicker from "./emoji-picker";
-
-interface Reaction {
- emoji: string;
- count: number;
- players: PlayerInfo[];
- hasReacted: boolean;
-}
+import { useMatchReactions, useToggleMatchReaction } from "../queries";
+import { useAuth } from "@/contexts/auth-context";
+import { Reaction } from "@/features/matches/server";
interface EmojiBarProps {
- reactions?: Reaction[];
+ matchId: string;
onReactionPress?: (emoji: string) => void;
}
-const EXAMPLE_DATA: Reaction[] = [
- {
- emoji: "👍",
- count: 1,
- players: [{ id: "dasfasdf", first_name: "Kyle", last_name: "Yohler" }],
- hasReacted: true,
- },
- {
- emoji: "❤️",
- count: 1,
- players: [{ id: "f3234f", first_name: "Salah", last_name: "Atiyeh" }],
- hasReacted: false,
- },
-];
-
const EmojiBar = ({
- reactions = EXAMPLE_DATA,
+ matchId,
onReactionPress,
}: EmojiBarProps) => {
+ const { user } = useAuth();
+ const { data: reactions } = useMatchReactions(matchId);
+ const toggleReaction = useToggleMatchReaction(matchId);
+
const [opened, { open, close }] = useDisclosure(false);
const [activeTab, setActiveTab] = useState(null);
const longPressTimeout = useRef(null);
@@ -61,10 +45,20 @@ const EmojiBar = ({
const handleReactionClick = (emoji: string) => {
handleLongPressEnd();
- onReactionPress?.(emoji);
+ if (onReactionPress) {
+ onReactionPress(emoji);
+ } else {
+ toggleReaction.mutate({ data: { matchId, emoji } });
+ }
};
- if (!reactions.length) return null;
+ const hasReacted = useCallback((reaction: Reaction) => {
+ return reaction.players.map(p => p.id).includes(user?.id || "");
+ }, []);
+
+ if (!reactions) return;
+
+ console.log(reactions)
return (
<>
@@ -73,8 +67,9 @@ const EmojiBar = ({
{reactions.map((reaction) => (
))}
- {})} />
+ toggleReaction.mutate({ data: { matchId, emoji } }))} />
close()}>
diff --git a/src/features/reactions/queries.ts b/src/features/reactions/queries.ts
new file mode 100644
index 0000000..4b0b978
--- /dev/null
+++ b/src/features/reactions/queries.ts
@@ -0,0 +1,34 @@
+import { useServerQuery, useServerMutation } from "@/lib/tanstack-query/hooks";
+import { getMatchReactions, toggleMatchReaction } from "@/features/matches/server";
+import { useQueryClient } from "@tanstack/react-query";
+import { matchKeys } from "@/features/matches/queries";
+
+export const reactionKeys = {
+ match: (matchId: string) => ['reactions', 'match', matchId] as const,
+};
+
+export const reactionQueries = {
+ match: (matchId: string) => ({
+ queryKey: reactionKeys.match(matchId),
+ queryFn: () => getMatchReactions({ data: matchId }),
+ }),
+};
+
+export const useMatchReactions = (matchId: string) =>
+ useServerQuery(reactionQueries.match(matchId));
+
+export const useToggleMatchReaction = (matchId: string) => {
+ const queryClient = useQueryClient();
+
+ return useServerMutation({
+ mutationFn: toggleMatchReaction,
+ onSuccess: () => {
+ queryClient.invalidateQueries({
+ queryKey: reactionKeys.match(matchId)
+ });
+ queryClient.invalidateQueries({
+ queryKey: matchKeys.reactions(matchId)
+ });
+ },
+ });
+};
\ No newline at end of file
diff --git a/src/features/teams/components/team-profile/index.tsx b/src/features/teams/components/team-profile/index.tsx
index 9e7d1eb..37889d7 100644
--- a/src/features/teams/components/team-profile/index.tsx
+++ b/src/features/teams/components/team-profile/index.tsx
@@ -1,10 +1,11 @@
-import { Box, Text } from "@mantine/core";
+import { Box, Divider, Text, Stack } from "@mantine/core";
import Header from "./header";
import SwipeableTabs from "@/components/swipeable-tabs";
import TournamentList from "@/features/tournaments/components/tournament-list";
import StatsOverview from "@/shared/components/stats-overview";
import { useTeam, useTeamMatches, useTeamStats } from "../../queries";
import MatchList from "@/features/matches/components/match-list";
+import PlayerList from "@/features/players/components/player-list";
interface ProfileProps {
id: string;
@@ -19,7 +20,17 @@ const TeamProfile = ({ id }: ProfileProps) => {
const tabs = [
{
label: "Overview",
- content: ,
+ content: <>
+
+ Players
+
+
+
+
+ Statistics
+
+
+ >,
},
{
label: "Matches",
diff --git a/src/features/tournaments/components/started-tournament/header.tsx b/src/features/tournaments/components/started-tournament/header.tsx
new file mode 100644
index 0000000..ef15152
--- /dev/null
+++ b/src/features/tournaments/components/started-tournament/header.tsx
@@ -0,0 +1,67 @@
+import { Group, Stack, ThemeIcon, Text, Flex } from "@mantine/core";
+import { Tournament } from "../../types";
+import Avatar from "@/components/avatar";
+import {
+ CalendarIcon,
+ MapPinIcon,
+ TrophyIcon,
+} from "@phosphor-icons/react";
+import { useMemo } from "react";
+
+const Header = ({ tournament }: { tournament: Tournament }) => {
+ const tournamentStart = useMemo(
+ () => new Date(tournament.start_time),
+ [tournament.start_time]
+ );
+
+ return (
+
+
+
+
+
+ {tournament.location && (
+
+
+
+
+
+ {tournament.location}
+
+
+ )}
+
+
+
+
+
+
+ {tournamentStart.toLocaleDateString(undefined, {
+ weekday: "short",
+ month: "short",
+ day: "numeric",
+ })}{" "}
+ at{" "}
+ {tournamentStart.toLocaleTimeString([], {
+ hour: "2-digit",
+ minute: "2-digit",
+ })}
+
+
+
+
+ );
+};
+
+export default Header;
\ No newline at end of file
diff --git a/src/features/tournaments/components/started-tournament/index.tsx b/src/features/tournaments/components/started-tournament/index.tsx
new file mode 100644
index 0000000..8bdb75a
--- /dev/null
+++ b/src/features/tournaments/components/started-tournament/index.tsx
@@ -0,0 +1,79 @@
+import { useMemo } from "react";
+import { Tournament } from "../../types";
+import { useAuth } from "@/contexts/auth-context";
+import { Box, Divider, Stack, Text, Card, Center } from "@mantine/core";
+import { Carousel } from "@mantine/carousel";
+import ListLink from "@/components/list-link";
+import { TreeStructureIcon, UsersIcon, ClockIcon } from "@phosphor-icons/react";
+import TeamListButton from "../upcoming-tournament/team-list-button";
+import RulesListButton from "../upcoming-tournament/rules-list-button";
+import MatchCard from "@/features/matches/components/match-card";
+import Header from "./header";
+
+const StartedTournament: React.FC<{ tournament: Tournament }> = ({
+ tournament,
+}) => {
+ const { roles } = useAuth();
+
+ const isAdmin = useMemo(() => roles.includes("Admin"), [roles]);
+
+ const startedMatches = useMemo(() =>
+ tournament.matches?.filter(match => match.status === "started") || [],
+ [tournament.matches]
+ );
+
+ return (
+
+
+
+ {startedMatches.length > 0 ? (
+
+
+ {startedMatches.map((match, index) => (
+
+
+
+
+
+ ))}
+
+
+ ) : (
+
+
+
+
+
+ No active matches
+
+
+
+
+ )}
+
+
+
+ {isAdmin && (
+
+ )}
+
+
+
+
+
+ );
+};
+
+export default StartedTournament;
\ No newline at end of file
diff --git a/src/hooks/use-server-events.ts b/src/hooks/use-server-events.ts
index b4af930..7fe36ad 100644
--- a/src/hooks/use-server-events.ts
+++ b/src/hooks/use-server-events.ts
@@ -2,19 +2,13 @@ import { useEffect, useRef } from "react";
import { useQueryClient } from "@tanstack/react-query";
import { Logger } from "@/lib/logger";
import { useAuth } from "@/contexts/auth-context";
-/*
-import { authClient } from "~/lib/auth-client";
-import { getIdeaFn } from "~/routes/-fn/getIdeaFn";
-import { useSession } from "~/lib/sessionContext";
-*/
+import { tournamentKeys, tournamentQueries } from "@/features/tournaments/queries";
-// Global state for new ideas notification
let newIdeasAvailable = false;
let newIdeasCallbacks: (() => void)[] = [];
const logger = new Logger('ServerEvents');
-// Event handler types for better type safety
type SSEEvent = {
type: string;
[key: string]: any;
@@ -22,29 +16,26 @@ type SSEEvent = {
type EventHandler = (event: SSEEvent, queryClient: ReturnType, currentSessionId?: string) => void;
-// Event handlers map - add new handlers here for easy extension
const eventHandlers: Record = {
"connected": () => {
logger.info("ServerEvents | New Connection");
},
- "ping": () => {
- // Keep-alive ping, no action needed
- },
+ "ping": () => {},
"test": (event, queryClient) => {
+
},
-
- // Add new event handlers here:
- // "idea-updated": (event, queryClient) => {
- // queryClient.invalidateQueries({ queryKey: ["ideas"] });
- // },
- // "vote-changed": (event, queryClient) => {
- // queryClient.invalidateQueries({ queryKey: ["idea", event.ideaId] });
- // },
+
+ "match": (event, queryClient) => {
+ console.log(event);
+
+ queryClient.invalidateQueries(tournamentQueries.details(event.tournamentId))
+ queryClient.invalidateQueries(tournamentQueries.current())
+ }
+
};
-// Functions to manage new ideas notification state
export function getNewIdeasAvailable(): boolean {
return newIdeasAvailable;
}
@@ -55,7 +46,6 @@ export function clearNewIdeasAvailable(): void {
export function subscribeToNewIdeas(callback: () => void): () => void {
newIdeasCallbacks.push(callback);
- // Return unsubscribe function
return () => {
const index = newIdeasCallbacks.indexOf(callback);
if (index > -1) {
@@ -84,7 +74,6 @@ export function useServerEvents() {
const eventSource = new EventSource(`/api/events/$`);
eventSource.onopen = () => {
- // Reset retry count on successful connection
retryCountRef.current = 0;
};
@@ -93,7 +82,6 @@ export function useServerEvents() {
const data: SSEEvent = JSON.parse(event.data);
logger.info("Event received", data);
- // Use the event handler pattern for extensible event processing
const handler = eventHandlers[data.type];
if (handler) {
handler(data, queryClient, user?.id);
@@ -109,13 +97,12 @@ export function useServerEvents() {
logger.error("SSE connection error", error);
eventSource.close();
- // Only retry if we should still be connecting and haven't exceeded max retries
if (shouldConnectRef.current && retryCountRef.current < 5) {
retryCountRef.current += 1;
const delay = Math.min(
1000 * Math.pow(2, retryCountRef.current - 1),
30000
- ); // Cap at 30 seconds
+ );
logger.info(
`SSE reconnection attempt ${retryCountRef.current}/5 in ${delay}ms`
diff --git a/src/lib/events/emitter.ts b/src/lib/events/emitter.ts
index 53e5959..c84f6b6 100644
--- a/src/lib/events/emitter.ts
+++ b/src/lib/events/emitter.ts
@@ -7,4 +7,10 @@ export type TestEvent = {
playerId: string;
};
-export type ServerEvent = TestEvent;
+export type MatchEvent = {
+ type: "match";
+ matchId: string;
+ tournamentId: string;
+}
+
+export type ServerEvent = TestEvent | MatchEvent;
diff --git a/src/lib/pocketbase/client.ts b/src/lib/pocketbase/client.ts
index a409f72..f4e3b3f 100644
--- a/src/lib/pocketbase/client.ts
+++ b/src/lib/pocketbase/client.ts
@@ -3,6 +3,7 @@ import { createPlayersService } from "./services/players";
import { createTournamentsService } from "./services/tournaments";
import { createTeamsService } from "./services/teams";
import { createMatchesService } from "./services/matches";
+import { createReactionsService } from "./services/reactions";
class PocketBaseAdminClient {
private pb: PocketBase;
@@ -31,6 +32,7 @@ class PocketBaseAdminClient {
Object.assign(this, createTeamsService(this.pb));
Object.assign(this, createTournamentsService(this.pb));
Object.assign(this, createMatchesService(this.pb));
+ Object.assign(this, createReactionsService(this.pb));
});
}
@@ -49,7 +51,8 @@ interface AdminClient
ReturnType,
ReturnType,
ReturnType,
- ReturnType {
+ ReturnType,
+ ReturnType {
authPromise: Promise;
}
diff --git a/src/lib/pocketbase/services/matches.ts b/src/lib/pocketbase/services/matches.ts
index de7dc04..d57721b 100644
--- a/src/lib/pocketbase/services/matches.ts
+++ b/src/lib/pocketbase/services/matches.ts
@@ -6,7 +6,6 @@ import { transformMatch } from "../util/transform-types";
export function createMatchesService(pb: PocketBase) {
return {
async getMatch(id: string): Promise {
- logger.info("PocketBase | Getting match", id);
const result = await pb.collection("matches").getOne(id, {
expand: "tournament, home, away",
});
@@ -15,7 +14,6 @@ export function createMatchesService(pb: PocketBase) {
// match Ids where the current lid is home_from_lid or away_from_lid
async getChildMatches(matchId: string): Promise<{ winner: Match | undefined, loser: Match | undefined }> {
- logger.info("PocketBase | Getting child matches", matchId);
const match = await this.getMatch(matchId);
if (!match) throw new Error("Match not found")
@@ -52,7 +50,7 @@ export function createMatchesService(pb: PocketBase) {
async updateMatch(id: string, data: Partial): Promise {
logger.info("PocketBase | Updating match", { id, data });
const result = await pb.collection("matches").update(id, data, {
- expand: 'home, away'
+ expand: 'home, away, tournament'
});
return transformMatch(result);
},
diff --git a/src/lib/pocketbase/services/players.ts b/src/lib/pocketbase/services/players.ts
index 940f80a..da705c5 100644
--- a/src/lib/pocketbase/services/players.ts
+++ b/src/lib/pocketbase/services/players.ts
@@ -80,7 +80,9 @@ export function createPlayersService(pb: PocketBase) {
},
async getPlayerMatches(playerId: string): Promise {
- const player = await pb.collection("players").getOne(playerId, {
+ console.log('----------------')
+ console.log(playerId)
+ const player = await pb.collection("players").getOne(playerId.trim(), {
expand: "teams",
});
diff --git a/src/lib/pocketbase/services/reactions.ts b/src/lib/pocketbase/services/reactions.ts
new file mode 100644
index 0000000..2c75b2b
--- /dev/null
+++ b/src/lib/pocketbase/services/reactions.ts
@@ -0,0 +1,35 @@
+import PocketBase from "pocketbase";
+import { transformReaction } from "../util/transform-types";
+
+export const createReactionsService = (pb: PocketBase) => ({
+ async getReactionsForMatch(matchId: string) {
+ const reactions = await pb.collection('reactions').getFullList({
+ filter: `match="${matchId}"`,
+ expand: 'player',
+ });
+ return reactions.map(transformReaction)
+ },
+
+ async getUserReaction(matchId: string, userId: string, emoji: string) {
+ try {
+ return await pb.collection('reactions').getFirstListItem(`match="${matchId}" && player="${userId}" && emoji="${emoji}"`);
+ } catch (error) {
+ return;
+ }
+ },
+
+ async createReaction(matchId: string, userId: string, emoji: string) {
+ const reaction = await pb.collection('reactions').create({
+ match: matchId,
+ player: userId,
+ emoji: emoji,
+ }, {
+ expand: 'player'
+ });
+ return transformReaction(reaction)
+ },
+
+ async deleteReaction(reactionId: string) {
+ return await pb.collection('reactions').delete(reactionId);
+ },
+});
\ No newline at end of file
diff --git a/src/lib/pocketbase/services/teams.ts b/src/lib/pocketbase/services/teams.ts
index 08ac0fc..948d980 100644
--- a/src/lib/pocketbase/services/teams.ts
+++ b/src/lib/pocketbase/services/teams.ts
@@ -7,7 +7,6 @@ import { Match } from "@/features/matches/types";
export function createTeamsService(pb: PocketBase) {
return {
async getTeamInfo(id: string): Promise {
- logger.info("PocketBase | Getting team info", id);
const result = await pb.collection("teams").getOne(id, {
fields: "id,name,primary_color,accent_color,logo",
expand: "players"
@@ -25,7 +24,6 @@ export function createTeamsService(pb: PocketBase) {
},
async getTeam(id: string): Promise {
- logger.info("PocketBase | Getting team", id);
const result = await pb.collection("teams").getOne(id, {
expand: "players, tournaments",
});
@@ -84,7 +82,6 @@ export function createTeamsService(pb: PocketBase) {
},
async getTeamStats(id: string): Promise {
- logger.info("PocketBase | Getting team stats", id);
try {
const result = await pb.collection("team_stats").getFirstListItem(`team_id="${id}"`);
return result as unknown as TeamStats;
diff --git a/src/lib/pocketbase/services/tournaments.ts b/src/lib/pocketbase/services/tournaments.ts
index d4d4619..9e00a5a 100644
--- a/src/lib/pocketbase/services/tournaments.ts
+++ b/src/lib/pocketbase/services/tournaments.ts
@@ -23,7 +23,7 @@ export function createTournamentsService(pb: PocketBase) {
.collection("tournaments")
.getFirstListItem('',
{
- expand: "teams, teams.players, matches, matches.tournament, matches.home, matches.away",
+ expand: "teams, teams.players, matches, matches.tournament, matches.home, matches.away, matches.home.players, matches.away.players",
sort: "-created",
}
);
@@ -104,10 +104,6 @@ export function createTournamentsService(pb: PocketBase) {
},
async getUnenrolledTeams(tournamentId: string): Promise {
try {
- logger.info(
- "PocketBase | Getting unenrolled teams for tournament",
- tournamentId
- );
const tournament = await pb
.collection("tournaments")
.getOne(tournamentId, {
diff --git a/src/lib/pocketbase/util/transform-types.ts b/src/lib/pocketbase/util/transform-types.ts
index c7789e7..31b950d 100644
--- a/src/lib/pocketbase/util/transform-types.ts
+++ b/src/lib/pocketbase/util/transform-types.ts
@@ -1,3 +1,4 @@
+import { Reaction } from "@/features/matches/server";
import { Match } from "@/features/matches/types";
import { Player, PlayerInfo } from "@/features/players/types";
import { Team, TeamInfo } from "@/features/teams/types";
@@ -149,3 +150,12 @@ export function transformTournament(record: any): Tournament {
matches
};
}
+
+export function transformReaction(record: any) {
+ return {
+ id: record.id,
+ emoji: record.emoji,
+ player: transformPlayerInfo(record.expand.player),
+ match: record.match
+ };
+}