feat(hackathon): improve auth flow and UX

- Add email activation requirement for signup (no auto-login)
- Add form validation with React Hook Form and Zod on signup page
- Update callback page to handle email confirmation and password reset redirects
- Fix token format in API interceptor (use access_token)
- Fix middleware to use SessionToken for auth check
- Add infinite scroll with IntersectionObserver on browse teams page
- Replace all internal <a href> with <Link> components
- Add useInfiniteTeams hook for paginated team browsing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Maulana Sodiqin
2025-11-28 17:40:34 +07:00
co-authored by Claude
parent f8d592d811
commit fa30849d03
10 changed files with 363 additions and 181 deletions
+12 -11
View File
@@ -1,5 +1,5 @@
import { SessionUser } from '@imphnen-frontend-service/utils';
import { hackathonApi } from '@imphnen-frontend-service/service';
import { hackathonApi, SessionToken } from '@imphnen-frontend-service/service';
import { LoaderFunctionArgs, redirect } from 'react-router';
const mappingPublicRoutes = [
@@ -37,9 +37,10 @@ export const middleware = async ({ request }: LoaderFunctionArgs) => {
const url = new URL(request.url);
const pathname = url.pathname;
// Get session from local storage (via SessionUser)
const session = SessionUser.get();
const isAuthenticated = !!session?.token?.access_token;
// Get token from cookies and user from local storage
const tokenData = SessionToken.get();
const user = SessionUser.get();
const isAuthenticated = !!tokenData?.token?.access_token;
// Allow to access the hackathon pages without authentication
if (mappingPublicPrefixRoutes.some((prefix) => pathname.startsWith(prefix))) {
@@ -71,7 +72,7 @@ export const middleware = async ({ request }: LoaderFunctionArgs) => {
// Skip onboarding check for onboarding routes themselves
if (!mappingOnboardingRoutes.includes(pathname)) {
try {
const userId = session?.user?.id;
const userId = user?.id;
if (!userId) {
return redirect('/auth/login');
}
@@ -85,8 +86,8 @@ export const middleware = async ({ request }: LoaderFunctionArgs) => {
if (cached && (now - cached.timestamp) < CACHE_DURATION) {
hasLocation = cached.hasLocation;
} else {
// First check session data (faster)
if (session?.user?.location) {
// First check user data (faster)
if (user?.location) {
hasLocation = true;
} else {
// Fetch from backend API
@@ -94,8 +95,8 @@ export const middleware = async ({ request }: LoaderFunctionArgs) => {
const response = await hackathonApi.get('/users/me');
hasLocation = !!response.data?.data?.location;
} catch {
// If API fails, check session data as fallback
hasLocation = !!session?.user?.location;
// If API fails, check user data as fallback
hasLocation = !!user?.location;
}
}
@@ -113,9 +114,9 @@ export const middleware = async ({ request }: LoaderFunctionArgs) => {
}
}
// Check route permissions using user data from session
// Check route permissions using user data
const userPermissions =
session?.user?.role?.permissions?.map?.((perm) => perm?.name) ?? [];
user?.role?.permissions?.map?.((perm) => perm?.name) ?? [];
const matchedRoute = mappingRoutePermissions.find(
(route) => route.path === pathname