random imporvements

This commit is contained in:
yohlo
2025-08-24 12:53:34 -05:00
parent 1015f63f7e
commit 466a3365f0
6 changed files with 154 additions and 48 deletions

View File

@@ -17,7 +17,7 @@ export const Route = createFileRoute('/_authed/tournaments/$tournamentId')({
header: {
collapsed: true,
withBackButton: true,
settingsLink: context.auth.roles?.includes("Admin") ? `/admin/tournaments/${params.tournamentId}` : undefined
settingsLink: context.auth.roles.includes("Admin") ? `/admin/tournaments/${params.tournamentId}` : undefined
},
refresh: {
toRefresh: tournamentQueries.details(params.tournamentId).queryKey,

View File

@@ -1,53 +1,159 @@
import {
ErrorComponent,
Link,
rootRouteId,
useMatch,
useRouter,
useNavigate,
} from '@tanstack/react-router'
import type { ErrorComponentProps } from '@tanstack/react-router'
import {
Box,
Button,
Text,
Title,
Stack,
Group,
Alert,
Collapse,
Code,
ThemeIcon
} from '@mantine/core'
import { useDisclosure } from '@mantine/hooks'
import { useEffect } from 'react'
import toast from '@/lib/sonner'
import { logger } from '@/lib/logger'
import { ExclamationMarkIcon, XCircleIcon } from '@phosphor-icons/react'
export function DefaultCatchBoundary({ error }: ErrorComponentProps) {
const router = useRouter()
const navigate = useNavigate()
const isRoot = useMatch({
strict: false,
select: (state) => state.id === rootRouteId,
})
const [detailsOpened, { toggle: toggleDetails }] = useDisclosure(false)
console.error('DefaultCatchBoundary Error:', error)
const errorMessage = error?.message || 'Unknown error'
const errorStack = error?.stack || 'No stack trace available'
useEffect(() => {
logger.error('DefaultCatchBoundary | ', error)
if (errorMessage.toLowerCase().includes('unauthenticated')) {
toast.error('You\'ve been logged out')
navigate({ to: '/login' })
return
}
}, [error, errorMessage, navigate])
if (errorMessage.toLowerCase().includes('unauthorized')) {
return (
<Box
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
minHeight: '50vh',
padding: 'var(--mantine-spacing-xl)',
}}
>
<Stack align="center" gap="lg">
<ThemeIcon color="red" size={80} radius="xl">
<XCircleIcon size={48} />
</ThemeIcon>
<Title order={2} ta="center">Access Denied</Title>
<Text size="lg" c="dimmed" ta="center">
You don't have permission to access this.
</Text>
<Group>
<Button
variant="light"
onClick={() => window.history.back()}
>
Go Back
</Button>
<Button
component={Link}
to="/"
variant="filled"
>
Home
</Button>
</Group>
</Stack>
</Box>
)
}
return (
<div className="min-w-0 flex-1 p-4 flex flex-col items-center justify-center gap-6">
<ErrorComponent error={error} />
<div className="flex gap-2 items-center flex-wrap">
<button
onClick={() => {
router.invalidate()
}}
className={`px-2 py-1 bg-gray-600 dark:bg-gray-700 rounded text-white uppercase font-extrabold`}
<Box
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
minHeight: '50vh',
padding: 'var(--mantine-spacing-xl)',
}}
>
<Stack align="center" gap="lg" maw={600}>
<ThemeIcon color="red" size={80} radius="xl">
<ExclamationMarkIcon size={48} />
</ThemeIcon>
<Title order={2} ta="center">Something went wrong</Title>
<Text size="lg" c="dimmed" ta="center">
There was an unexpected error. Please try again later.
</Text>
<Alert
variant="light"
color="red"
title="Error Details"
w="100%"
>
Try Again
</button>
{isRoot ? (
<Link
to="/"
className={`px-2 py-1 bg-gray-600 dark:bg-gray-700 rounded text-white uppercase font-extrabold`}
<Text mb="sm">{errorMessage}</Text>
<Button
variant="subtle"
size="compact-sm"
onClick={toggleDetails}
>
Home
</Link>
) : (
<Link
to="/"
className={`px-2 py-1 bg-gray-600 dark:bg-gray-700 rounded text-white uppercase font-extrabold`}
onClick={(e) => {
e.preventDefault()
window.history.back()
}}
{detailsOpened ? 'Hide' : 'Show'} technical details
</Button>
<Collapse in={detailsOpened}>
<Code block mt="md" p="md">
{errorStack}
</Code>
</Collapse>
</Alert>
<Group>
<Button
variant="light"
onClick={() => router.invalidate()}
>
Go Back
</Link>
)}
</div>
</div>
Try Again
</Button>
{isRoot ? (
<Button
component={Link}
to="/"
variant="filled"
>
Home
</Button>
) : (
<Button
variant="filled"
onClick={() => window.history.back()}
>
Go Back
</Button>
)}
</Group>
</Stack>
</Box>
)
}

View File

@@ -1,4 +1,4 @@
import { Box, Button, Text } from "@mantine/core";
import { Box, Button, Text, Title } from "@mantine/core";
import Header from "./header";
import { testEvent } from "@/utils/test-event";
import { Player } from "@/features/players/types";
@@ -17,10 +17,12 @@ const Profile = ({ player }: ProfileProps) => {
},
{
label: "Teams",
content: <Text p="md">Panel 2 content</Text>
content: <>
<TeamList teams={player.teams || []} />
</>
},
{
label: "Stats",
label: "Tournaments",
content: <Text p="md">Panel 3 content</Text>
}
];
@@ -28,13 +30,7 @@ const Profile = ({ player }: ProfileProps) => {
return <>
<Header player={player} />
<Box m='sm' mt='lg'>
<SwipeableTabs
tabs={tabs}
defaultTab={0}
onTabChange={(index, tab) => {
console.log(`Switched to ${tab.label} tab`);
}}
/>
<SwipeableTabs tabs={tabs} />
</Box>
</>;
};

View File

@@ -1,13 +1,16 @@
import { setUserMetadata, superTokensFunctionMiddleware } from "@/utils/supertokens";
import { setUserMetadata, superTokensFunctionMiddleware, verifySuperTokensSession } from "@/utils/supertokens";
import { createServerFn } from "@tanstack/react-start";
import { playerInputSchema, playerUpdateSchema } from "@/features/players/types";
import { pbAdmin } from "@/lib/pocketbase/client";
import { z } from "zod";
import { logger } from ".";
import { getWebRequest } from "@tanstack/react-start/server";
export const fetchMe = createServerFn()
.middleware([superTokensFunctionMiddleware])
.handler(async ({ context }) => {
.handler(async ({ response }) => {
const request = getWebRequest();
const { context } = await verifySuperTokensSession(request, response);
if (!context || !context.userAuthId) return { user: undefined, roles: [], metadata: {} };
try {

View File

@@ -28,7 +28,6 @@ export function createTournamentsService(pb: PocketBase) {
sort: "-created",
});
console.log(result);
return result.map(transformTournament);
},
async createTournament(data: TournamentInput): Promise<Tournament> {

View File

@@ -10,7 +10,7 @@ import { refreshSession } from "supertokens-node/recipe/session";
const logger = new Logger('Middleware');
const verifySuperTokensSession = async (request: Request, response?: ServerFnResponseType) => {
export const verifySuperTokensSession = async (request: Request, response?: ServerFnResponseType) => {
const session = await getSessionForStart(request, { sessionRequired: false });
if (session?.needsRefresh && response) {
@@ -44,9 +44,10 @@ const verifySuperTokensSession = async (request: Request, response?: ServerFnRes
export const superTokensRequestMiddleware = createMiddleware({ type: 'request' })
.server(async ({ next, request }) => {
const session = await verifySuperTokensSession(request);
if (!session.context.userAuthId) {
logger.error('Unauthenticated user in API call.', session.context)
throw new Error("Unauthenticated");
}
const context = {
@@ -65,6 +66,7 @@ export const superTokensFunctionMiddleware = createMiddleware({ type: 'function'
if (!session.context.userAuthId) {
logger.error('Unauthenticated user in server function.', session.context)
throw new Error("Unauthenticated");
}
const context = {