diff --git a/pb_migrations/1759344931_deleted_player_badges_view.js b/pb_migrations/1759344931_deleted_player_badges_view.js
deleted file mode 100644
index de1bd90..0000000
--- a/pb_migrations/1759344931_deleted_player_badges_view.js
+++ /dev/null
@@ -1,187 +0,0 @@
-///
-migrate((app) => {
- const collection = app.findCollectionByNameOrId("pbc_5062686152");
-
- return app.delete(collection);
-}, (app) => {
- const collection = new Collection({
- "createRule": null,
- "deleteRule": null,
- "fields": [
- {
- "autogeneratePattern": "",
- "hidden": false,
- "id": "text3208210256",
- "max": 0,
- "min": 0,
- "name": "id",
- "pattern": "^[a-z0-9]+$",
- "presentable": false,
- "primaryKey": true,
- "required": true,
- "system": true,
- "type": "text"
- },
- {
- "cascadeDelete": false,
- "collectionId": "pbc_3072146508",
- "hidden": false,
- "id": "relation2582050271",
- "maxSelect": 1,
- "minSelect": 0,
- "name": "player_id",
- "presentable": false,
- "required": false,
- "system": false,
- "type": "relation"
- },
- {
- "cascadeDelete": false,
- "collectionId": "pbc_1340419796",
- "hidden": false,
- "id": "relation4154639100",
- "maxSelect": 1,
- "minSelect": 0,
- "name": "badge_id",
- "presentable": false,
- "required": false,
- "system": false,
- "type": "relation"
- },
- {
- "autogeneratePattern": "",
- "hidden": false,
- "id": "_clone_GhrR",
- "max": 0,
- "min": 0,
- "name": "badge_name",
- "pattern": "",
- "presentable": false,
- "primaryKey": false,
- "required": true,
- "system": false,
- "type": "text"
- },
- {
- "autogeneratePattern": "",
- "hidden": false,
- "id": "_clone_DEaW",
- "max": 0,
- "min": 0,
- "name": "badge_description",
- "pattern": "",
- "presentable": false,
- "primaryKey": false,
- "required": true,
- "system": false,
- "type": "text"
- },
- {
- "hidden": false,
- "id": "_clone_MHmw",
- "maxSelect": 1,
- "name": "badge_type",
- "presentable": false,
- "required": true,
- "system": false,
- "type": "select",
- "values": [
- "tournament_participation",
- "tournament_placement",
- "performance",
- "overtime",
- "match_milestone"
- ]
- },
- {
- "autogeneratePattern": "",
- "hidden": false,
- "id": "_clone_11YE",
- "max": 50,
- "min": 0,
- "name": "badge_icon",
- "pattern": "",
- "presentable": false,
- "primaryKey": false,
- "required": false,
- "system": false,
- "type": "text"
- },
- {
- "autogeneratePattern": "",
- "hidden": false,
- "id": "_clone_qAJu",
- "max": 50,
- "min": 0,
- "name": "badge_color",
- "pattern": "",
- "presentable": false,
- "primaryKey": false,
- "required": false,
- "system": false,
- "type": "text"
- },
- {
- "hidden": false,
- "id": "_clone_giOf",
- "name": "is_progressive",
- "presentable": false,
- "required": false,
- "system": false,
- "type": "bool"
- },
- {
- "hidden": false,
- "id": "json3212413036",
- "maxSize": 1,
- "name": "current_progress",
- "presentable": false,
- "required": false,
- "system": false,
- "type": "json"
- },
- {
- "hidden": false,
- "id": "json4171899439",
- "maxSize": 1,
- "name": "target_progress",
- "presentable": false,
- "required": false,
- "system": false,
- "type": "json"
- },
- {
- "hidden": false,
- "id": "json3435813110",
- "maxSize": 1,
- "name": "is_earned",
- "presentable": false,
- "required": false,
- "system": false,
- "type": "json"
- },
- {
- "hidden": false,
- "id": "_clone_Q7lC",
- "max": "",
- "min": "",
- "name": "earned_at",
- "presentable": false,
- "required": false,
- "system": false,
- "type": "date"
- }
- ],
- "id": "pbc_5062686152",
- "indexes": [],
- "listRule": null,
- "name": "player_badges_view",
- "system": false,
- "type": "view",
- "updateRule": null,
- "viewQuery": "\n SELECT\n (p.id || '_' || b.id) as id,\n p.id as player_id,\n b.id as badge_id,\n b.name as badge_name,\n b.description as badge_description,\n b.type as badge_type,\n b.icon as badge_icon,\n b.color as badge_color,\n b.is_progressive,\n COALESCE(pbp.current_progress, 0) as current_progress,\n COALESCE(pbp.target_progress, b.progress_target, 1) as target_progress,\n COALESCE(pbp.is_earned, false) as is_earned,\n pbp.earned_at\n FROM players p\n CROSS JOIN badges b\n LEFT JOIN player_badge_progress pbp ON pbp.player_id = p.id AND pbp.badge_id = b.id\n ",
- "viewRule": null
- });
-
- return app.save(collection);
-})
diff --git a/src/app/routeTree.gen.ts b/src/app/routeTree.gen.ts
index b8bfcc6..553c25a 100644
--- a/src/app/routeTree.gen.ts
+++ b/src/app/routeTree.gen.ts
@@ -16,6 +16,7 @@ import { Route as AuthedRouteImport } from './routes/_authed'
import { Route as AuthedIndexRouteImport } from './routes/_authed/index'
import { Route as AuthedStatsRouteImport } from './routes/_authed/stats'
import { Route as AuthedSettingsRouteImport } from './routes/_authed/settings'
+import { Route as AuthedBadgesRouteImport } from './routes/_authed/badges'
import { Route as AuthedAdminRouteImport } from './routes/_authed/admin'
import { Route as AuthedTournamentsIndexRouteImport } from './routes/_authed/tournaments/index'
import { Route as AuthedAdminIndexRouteImport } from './routes/_authed/admin/index'
@@ -76,6 +77,11 @@ const AuthedSettingsRoute = AuthedSettingsRouteImport.update({
path: '/settings',
getParentRoute: () => AuthedRoute,
} as any)
+const AuthedBadgesRoute = AuthedBadgesRouteImport.update({
+ id: '/badges',
+ path: '/badges',
+ getParentRoute: () => AuthedRoute,
+} as any)
const AuthedAdminRoute = AuthedAdminRouteImport.update({
id: '/admin',
path: '/admin',
@@ -215,6 +221,7 @@ export interface FileRoutesByFullPath {
'/logout': typeof LogoutRoute
'/refresh-session': typeof RefreshSessionRoute
'/admin': typeof AuthedAdminRouteWithChildren
+ '/badges': typeof AuthedBadgesRoute
'/settings': typeof AuthedSettingsRoute
'/stats': typeof AuthedStatsRoute
'/': typeof AuthedIndexRoute
@@ -247,6 +254,7 @@ export interface FileRoutesByTo {
'/login': typeof LoginRoute
'/logout': typeof LogoutRoute
'/refresh-session': typeof RefreshSessionRoute
+ '/badges': typeof AuthedBadgesRoute
'/settings': typeof AuthedSettingsRoute
'/stats': typeof AuthedStatsRoute
'/': typeof AuthedIndexRoute
@@ -282,6 +290,7 @@ export interface FileRoutesById {
'/logout': typeof LogoutRoute
'/refresh-session': typeof RefreshSessionRoute
'/_authed/admin': typeof AuthedAdminRouteWithChildren
+ '/_authed/badges': typeof AuthedBadgesRoute
'/_authed/settings': typeof AuthedSettingsRoute
'/_authed/stats': typeof AuthedStatsRoute
'/_authed/': typeof AuthedIndexRoute
@@ -317,6 +326,7 @@ export interface FileRouteTypes {
| '/logout'
| '/refresh-session'
| '/admin'
+ | '/badges'
| '/settings'
| '/stats'
| '/'
@@ -349,6 +359,7 @@ export interface FileRouteTypes {
| '/login'
| '/logout'
| '/refresh-session'
+ | '/badges'
| '/settings'
| '/stats'
| '/'
@@ -383,6 +394,7 @@ export interface FileRouteTypes {
| '/logout'
| '/refresh-session'
| '/_authed/admin'
+ | '/_authed/badges'
| '/_authed/settings'
| '/_authed/stats'
| '/_authed/'
@@ -481,6 +493,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof AuthedSettingsRouteImport
parentRoute: typeof AuthedRoute
}
+ '/_authed/badges': {
+ id: '/_authed/badges'
+ path: '/badges'
+ fullPath: '/badges'
+ preLoaderRoute: typeof AuthedBadgesRouteImport
+ parentRoute: typeof AuthedRoute
+ }
'/_authed/admin': {
id: '/_authed/admin'
path: '/admin'
@@ -687,6 +706,7 @@ const AuthedAdminRouteWithChildren = AuthedAdminRoute._addFileChildren(
interface AuthedRouteChildren {
AuthedAdminRoute: typeof AuthedAdminRouteWithChildren
+ AuthedBadgesRoute: typeof AuthedBadgesRoute
AuthedSettingsRoute: typeof AuthedSettingsRoute
AuthedStatsRoute: typeof AuthedStatsRoute
AuthedIndexRoute: typeof AuthedIndexRoute
@@ -699,6 +719,7 @@ interface AuthedRouteChildren {
const AuthedRouteChildren: AuthedRouteChildren = {
AuthedAdminRoute: AuthedAdminRouteWithChildren,
+ AuthedBadgesRoute: AuthedBadgesRoute,
AuthedSettingsRoute: AuthedSettingsRoute,
AuthedStatsRoute: AuthedStatsRoute,
AuthedIndexRoute: AuthedIndexRoute,
diff --git a/src/app/routes/_authed/badges.tsx b/src/app/routes/_authed/badges.tsx
new file mode 100644
index 0000000..c16290f
--- /dev/null
+++ b/src/app/routes/_authed/badges.tsx
@@ -0,0 +1,34 @@
+import BadgeStatsTable from '@/features/badges/components/badge-stats-table';
+import BadgeStatsTableSkeleton from '@/features/badges/components/badge-stats-table-skeleton';
+import { badgeQueries, useAllBadges } from '@/features/badges/queries';
+import PlayerStatsTableSkeleton from '@/features/players/components/player-stats-table-skeleton';
+import { prefetchServerQuery } from '@/lib/tanstack-query/utils/prefetch';
+import { createFileRoute } from '@tanstack/react-router';
+import { Suspense } from 'react';
+
+export const Route = createFileRoute('/_authed/badges')({
+ component: Badges,
+ beforeLoad: ({ context }) => {
+ const queryClient = context.queryClient;
+ prefetchServerQuery(queryClient, badgeQueries.allBadges());
+ },
+ loader: () => ({
+ withPadding: false,
+ fullWidth: true,
+ header: {
+ title: 'All Badges',
+ withBackButton: true,
+ },
+ refresh: [badgeQueries.allBadges().queryKey],
+ }),
+});
+
+function Badges() {
+ return (
+ }>
+
+
+
+
+ );
+}
diff --git a/src/features/badges/components/badge-showcase.tsx b/src/features/badges/components/badge-showcase.tsx
index 074af79..41b2627 100644
--- a/src/features/badges/components/badge-showcase.tsx
+++ b/src/features/badges/components/badge-showcase.tsx
@@ -18,15 +18,15 @@ interface BadgeDisplay {
interface BadgeIconProps {
badge: Badge;
- earned: boolean;
+ filled: boolean;
}
-const BadgeIcon = ({ badge, earned, size = 48 }: BadgeIconProps & { size?: number }) => {
+export const BadgeIcon = ({ badge, filled, size = 48 }: BadgeIconProps & { size?: number }) => {
const [imageError, setImageError] = useState(false);
const imagePath = `/static/img/${badge.key}.png`;
if (imageError) {
- return earned ? (
+ return filled ? (
setImageError(true)}
style={{
objectFit: 'contain',
- opacity: earned ? 1 : 0.4,
+ opacity: filled ? 1 : 0.4,
}}
/>
);
@@ -218,7 +218,7 @@ const BadgeShowcase = ({ playerId }: BadgeShowcaseProps) => {
zIndex: 1,
}}
>
-
+
{showStack && (
{
-
+
diff --git a/src/features/badges/components/badge-stats-table-skeleton.tsx b/src/features/badges/components/badge-stats-table-skeleton.tsx
new file mode 100644
index 0000000..f4044fa
--- /dev/null
+++ b/src/features/badges/components/badge-stats-table-skeleton.tsx
@@ -0,0 +1,67 @@
+import {
+ Stack,
+ Container,
+ Box,
+ Divider,
+ Grid,
+ Skeleton,
+} from '@mantine/core';
+
+const BadgeStatsTableSkeleton = () => {
+ return (
+
+
+
+ {Array.from({ length: 15 }).map((_, index) => (
+ = 14}
+ />
+ ))}
+
+
+
+ );
+};
+
+export default BadgeStatsTableSkeleton;
+
+interface BadgeStatRowSkeletonProps {
+ isLastRow: boolean;
+}
+
+const BadgeStatRowSkeleton: React.FC = ({
+ isLastRow,
+}) => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {!isLastRow && }
+
+ );
+};
diff --git a/src/features/badges/components/badge-stats-table.tsx b/src/features/badges/components/badge-stats-table.tsx
new file mode 100644
index 0000000..90aeb8b
--- /dev/null
+++ b/src/features/badges/components/badge-stats-table.tsx
@@ -0,0 +1,177 @@
+import {
+ Stack,
+ Container,
+ Title,
+ Box,
+ Divider,
+ Text,
+ Grid,
+ UnstyledButton,
+} from '@mantine/core';
+import { useAllBadges, useAllEarnedBadges } from '../queries';
+import { BadgeIcon } from './badge-showcase';
+import { useMemo } from 'react';
+import { Badge, EarnedBadge } from '../types';
+import { useAllPlayers } from '@/features/teams/hooks/use-available-players';
+import { useSheet } from '@/hooks/use-sheet';
+import Sheet from '@/components/sheet/sheet';
+import PlayerList from '@/features/players/components/player-list';
+import { useAuth } from '@/contexts/auth-context';
+import { Player } from '@/features/players/types';
+
+const BadgeStatsTable = () => {
+ const { data: allBadges } = useAllBadges();
+ const { data: allEarnedBadges } = useAllEarnedBadges();
+ const { data: allPlayers } = useAllPlayers();
+ const totalNumPlayers = allPlayers?.length || 0;
+
+ const groupedEarnedBadges = useMemo(() => {
+ const returnDict = new Map();
+ allEarnedBadges?.forEach((earnedBadge) => {
+ if (!returnDict.has(earnedBadge.badge)) {
+ returnDict.set(earnedBadge.badge, []);
+ }
+ returnDict.get(earnedBadge.badge)!.push(earnedBadge);
+ });
+ return returnDict;
+ }, [allEarnedBadges]);
+
+ if (allBadges.length === 0) {
+ return (
+
+
+
+ No Badges Available
+
+
+
+ );
+ }
+
+ return (
+
+
+
+ {allBadges.map((badge, index) => (
+ = allBadges.length - 1}
+ />
+ ))}
+
+
+
+ );
+};
+
+export default BadgeStatsTable;
+
+interface BadgeStatRowProps {
+ badge: Badge;
+ totalNumPlayers: number;
+ earnedBadges: EarnedBadge[];
+ isLastRow: boolean;
+}
+
+const BadgeStatRow: React.FC = ({
+ badge,
+ totalNumPlayers,
+ earnedBadges,
+ isLastRow,
+}) => {
+ const badgeSheet = useSheet();
+ const { user } = useAuth();
+
+ const playerNamesBlurb = useMemo(() => {
+ if (earnedBadges.length === 0) return 'No players yet';
+
+ const currentUserHasBadge = earnedBadges.some(
+ (eb) => eb.player.id === user?.id
+ );
+
+ const otherPlayers = earnedBadges.filter(
+ (eb) => eb.player.id !== user?.id
+ );
+
+ const displayPlayers = currentUserHasBadge
+ ? otherPlayers.slice(0, 2)
+ : earnedBadges.slice(0, 3);
+
+ const names = displayPlayers.map((eb) => eb.player.first_name);
+
+ if (currentUserHasBadge) {
+ const remaining = earnedBadges.length - 1 - names.length;
+ if (names.length === 0 && remaining === 0) {
+ return 'You';
+ } else if (names.length === 0 && remaining > 0) {
+ return `You and ${remaining} other${remaining > 1 ? 's' : ''}`;
+ } else if (remaining > 0) {
+ return `You, ${names.join(', ')} and ${remaining} other${remaining > 1 ? 's' : ''}`;
+ } else {
+ return `You${names.length > 0 ? ` and ${names.join(', ')}` : ''}`;
+ }
+ } else {
+ const remaining = earnedBadges.length - names.length;
+ if (remaining > 0) {
+ return `${names.join(', ')} and ${remaining} other${remaining > 1 ? 's' : ''}`;
+ } else {
+ return names.join(', ');
+ }
+ }
+ }, [earnedBadges, user?.id]);
+
+ const playersForList: Player[] = useMemo(() => {
+ return earnedBadges.map((eb) => ({
+ id: eb.player.id,
+ first_name: eb.player.first_name,
+ last_name: eb.player.last_name,
+ } as Player));
+ }, [earnedBadges]);
+
+ return (
+
+
+
+
+
+
+
+
+ {badge.name}
+ {badge.description}
+
+ {playerNamesBlurb}
+
+
+
+
+
+
+ {(
+ ((earnedBadges?.length ?? 0) / totalNumPlayers) *
+ 100
+ ).toFixed(0)}
+ %
+
+ of players
+
+
+
+
+
+
+
+ {!isLastRow && }
+
+ );
+};
diff --git a/src/features/badges/queries.ts b/src/features/badges/queries.ts
index 6428f9d..80bc271 100644
--- a/src/features/badges/queries.ts
+++ b/src/features/badges/queries.ts
@@ -1,9 +1,10 @@
import { useServerSuspenseQuery } from "@/lib/tanstack-query/hooks";
-import { getPlayerBadges, getAllBadges } from "./server";
+import { getPlayerBadges, getAllBadges, getAllEarnedBadges } from "./server";
export const badgeKeys = {
playerBadges: (playerId: string) => ['badges', 'player', playerId],
allBadges: () => ['badges', 'all'],
+ allEarnedBadges: () => ['badges', 'earned'],
};
export const badgeQueries = {
@@ -15,6 +16,10 @@ export const badgeQueries = {
queryKey: badgeKeys.allBadges(),
queryFn: async () => await getAllBadges()
}),
+ allEarnedBadges: () => ({
+ queryKey: badgeKeys.allEarnedBadges(),
+ queryFn: async () => await getAllEarnedBadges(),
+ }),
};
export const usePlayerBadges = (playerId: string) =>
@@ -22,3 +27,6 @@ export const usePlayerBadges = (playerId: string) =>
export const useAllBadges = () =>
useServerSuspenseQuery(badgeQueries.allBadges());
+
+export const useAllEarnedBadges = () =>
+ useServerSuspenseQuery(badgeQueries.allEarnedBadges());
diff --git a/src/features/badges/server.ts b/src/features/badges/server.ts
index 8d34ef0..23c88df 100644
--- a/src/features/badges/server.ts
+++ b/src/features/badges/server.ts
@@ -23,6 +23,10 @@ export const getAllBadges = createServerFn()
toServerResult(() => pbAdmin.listBadges())
);
+export const getAllEarnedBadges = createServerFn()
+ .middleware([superTokensFunctionMiddleware])
+ .handler(async () => toServerResult(() => pbAdmin.listEarnedBadges()));
+
export const awardManualBadge = createServerFn()
.inputValidator(z.object({
playerId: z.string(),
diff --git a/src/features/badges/types.ts b/src/features/badges/types.ts
index a71cf6c..3821521 100644
--- a/src/features/badges/types.ts
+++ b/src/features/badges/types.ts
@@ -1,3 +1,5 @@
+import { PlayerInfo } from '../players/types';
+
export interface BadgeInfo {
id: string;
name: string;
@@ -23,3 +25,13 @@ export interface BadgeProgress {
created: string;
updated: string;
}
+
+export interface EarnedBadge {
+ id: string;
+ badge: string;
+ player: PlayerInfo;
+ progress: number;
+ earned: boolean;
+ created: string;
+ updated: string;
+}
diff --git a/src/features/players/components/profile/index.tsx b/src/features/players/components/profile/index.tsx
index ff524bb..284059b 100644
--- a/src/features/players/components/profile/index.tsx
+++ b/src/features/players/components/profile/index.tsx
@@ -1,5 +1,6 @@
-import { Box, Stack, Text, Divider, Group, Button } from "@mantine/core";
+import { Box, Stack, Text, Divider, Group, Button, Anchor } from "@mantine/core";
import { Suspense, useState, useDeferredValue } from "react";
+import { Link } from "@tanstack/react-router";
import Header from "./header";
import SwipeableTabs from "@/components/swipeable-tabs";
import { usePlayer, usePlayerMatches, usePlayerStats } from "../../queries";
@@ -51,7 +52,12 @@ const Profile = ({ id }: ProfileProps) => {
label: "Overview",
content: <>
- Badges
+
+ Badges
+
+ View all badges
+
+
}>
diff --git a/src/features/teams/hooks/use-available-players.ts b/src/features/teams/hooks/use-available-players.ts
index 352b301..4061a91 100644
--- a/src/features/teams/hooks/use-available-players.ts
+++ b/src/features/teams/hooks/use-available-players.ts
@@ -16,13 +16,13 @@ export const playerQueries = {
export const useAvailablePlayers = (excludedPlayerIds: string[] = []) => {
const { data: allPlayers } = useServerSuspenseQuery(playerQueries.all());
-
+
const availablePlayers = useMemo(() => {
if (!allPlayers) return [];
-
+
return allPlayers.filter(player =>
- !excludedPlayerIds.includes(player.id) &&
- player.first_name &&
+ !excludedPlayerIds.includes(player.id) &&
+ player.first_name &&
player.last_name
);
}, [allPlayers, excludedPlayerIds]);
@@ -40,4 +40,6 @@ export const useAvailablePlayers = (excludedPlayerIds: string[] = []) => {
playerOptions,
allPlayers
};
-};
\ No newline at end of file
+};
+
+export const useAllPlayers = () => useServerSuspenseQuery(playerQueries.all());
diff --git a/src/lib/pocketbase/services/badges.ts b/src/lib/pocketbase/services/badges.ts
index 0268e52..e4b5261 100644
--- a/src/lib/pocketbase/services/badges.ts
+++ b/src/lib/pocketbase/services/badges.ts
@@ -1,6 +1,6 @@
import PocketBase from "pocketbase";
-import { Badge, BadgeProgress } from "@/features/badges/types";
-import { transformBadge, transformBadgeProgress } from "@/lib/pocketbase/util/transform-types";
+import { Badge, BadgeProgress, EarnedBadge } from "@/features/badges/types";
+import { transformBadge, transformBadgeProgress, transformEarnedBadge } from "@/lib/pocketbase/util/transform-types";
export interface PlayerStats {
player_id: string;
@@ -41,6 +41,14 @@ export function createBadgesService(pb: PocketBase) {
return results.map(transformBadgeProgress);
},
+ async listEarnedBadges(): Promise {
+ const results = await pb.collection("badge_progress").getFullList({
+ filter: `earned = true`,
+ expand: 'player',
+ });
+ return results.map(transformEarnedBadge);
+ },
+
async createBadgeProgress(data: {
badge: string;
player: string;
diff --git a/src/lib/pocketbase/util/transform-types.ts b/src/lib/pocketbase/util/transform-types.ts
index 900de8f..2733d05 100644
--- a/src/lib/pocketbase/util/transform-types.ts
+++ b/src/lib/pocketbase/util/transform-types.ts
@@ -2,7 +2,7 @@ import { Match } from "@/features/matches/types";
import { Player, PlayerInfo } from "@/features/players/types";
import { Team, TeamInfo } from "@/features/teams/types";
import { Tournament, TournamentInfo } from "@/features/tournaments/types";
-import { Badge, BadgeInfo, BadgeProgress } from "@/features/badges/types";
+import { Badge, BadgeInfo, BadgeProgress, EarnedBadge } from "@/features/badges/types";
import { Activity } from "../services/activities";
// pocketbase does this weird thing with relations where it puts them under a seperate "expand" field
@@ -312,6 +312,18 @@ export function transformBadgeProgress(record: any): BadgeProgress {
};
}
+export function transformEarnedBadge(record: any): EarnedBadge {
+ return {
+ id: record.id,
+ badge: record.badge,
+ player: record.expand?.player ? transformPlayerInfo(record.expand.player) : record.player,
+ progress: record.progress,
+ earned: record.earned,
+ created: record.created,
+ updated: record.updated,
+ };
+}
+
export function transformActivity(record: any): Activity {
return {
id: record.id,
diff --git a/src/lib/tanstack-query/hooks/index.ts b/src/lib/tanstack-query/hooks/index.ts
index 0a6004d..b9ac20f 100644
--- a/src/lib/tanstack-query/hooks/index.ts
+++ b/src/lib/tanstack-query/hooks/index.ts
@@ -1,4 +1,4 @@
export * from './use-optimistic-mutation';
export * from './use-server-mutation';
export * from './use-server-query';
-export * from './user-server-suspense-query';
\ No newline at end of file
+export * from './use-server-suspense-query';
diff --git a/src/lib/tanstack-query/hooks/user-server-suspense-query.ts b/src/lib/tanstack-query/hooks/use-server-suspense-query.ts
similarity index 100%
rename from src/lib/tanstack-query/hooks/user-server-suspense-query.ts
rename to src/lib/tanstack-query/hooks/use-server-suspense-query.ts