fix(login): replace useSearchParams with native URLSearchParams + loading spinner
- useSearchParams can lose params during re-renders, causing blank page - Use native window.location.search + URLSearchParams instead (always accessible) - Add oauthProcessing spinner state so user sees 'Menyelesaikan login...' instead of blank page while /auth/me is being called Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,23 +1,28 @@
|
|||||||
import { useState, useEffect, useRef } from "react";
|
import { useState, useEffect, useRef } from "react";
|
||||||
import { Link, useNavigate, useSearchParams } from "react-router-dom";
|
import { Link, useNavigate } from "react-router-dom";
|
||||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
import { AuthForm } from "@/components/auth-form";
|
import { AuthForm } from "@/components/auth-form";
|
||||||
import { apiClient, setAuthToken } from "@/lib/api-client";
|
import { apiClient, setAuthToken } from "@/lib/api-client";
|
||||||
import { useAuthStore } from "@/store/auth-store";
|
import { useAuthStore } from "@/store/auth-store";
|
||||||
|
|
||||||
|
function getUrlParam(name: string): string | null {
|
||||||
|
return new URLSearchParams(window.location.search).get(name);
|
||||||
|
}
|
||||||
|
|
||||||
export function LoginPage() {
|
export function LoginPage() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const setUser = useAuthStore((state) => state.setUser);
|
const setUser = useAuthStore((state) => state.setUser);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [searchParams] = useSearchParams();
|
|
||||||
const oauthTokenConsumed = useRef(false);
|
const oauthTokenConsumed = useRef(false);
|
||||||
|
const [oauthProcessing, setOauthProcessing] = useState(false);
|
||||||
|
|
||||||
// Handle OAuth callback: the API redirects to /login?token=<session_token>
|
// Handle OAuth callback: the API redirects to /login?token=<session_token>
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const token = searchParams.get("token");
|
const token = getUrlParam("token");
|
||||||
if (!token || oauthTokenConsumed.current) return;
|
if (!token || oauthTokenConsumed.current) return;
|
||||||
oauthTokenConsumed.current = true;
|
oauthTokenConsumed.current = true;
|
||||||
|
setOauthProcessing(true);
|
||||||
|
|
||||||
// Store token for future API calls and fetch user
|
// Store token for future API calls and fetch user
|
||||||
setAuthToken(token);
|
setAuthToken(token);
|
||||||
@@ -31,12 +36,13 @@ export function LoginPage() {
|
|||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
setAuthToken(null);
|
setAuthToken(null);
|
||||||
|
setOauthProcessing(false);
|
||||||
setError(err instanceof Error ? err.message : "Google login gagal");
|
setError(err instanceof Error ? err.message : "Google login gagal");
|
||||||
});
|
});
|
||||||
}, [searchParams, setUser, queryClient, navigate]);
|
}, [setUser, queryClient, navigate]);
|
||||||
|
|
||||||
// Show OAuth error from query param
|
// Show OAuth error from query param
|
||||||
const oauthError = searchParams.get("error");
|
const oauthError = getUrlParam("error");
|
||||||
const meQuery = useQuery({
|
const meQuery = useQuery({
|
||||||
queryKey: ["auth", "me"],
|
queryKey: ["auth", "me"],
|
||||||
queryFn: () => apiClient.getMe(),
|
queryFn: () => apiClient.getMe(),
|
||||||
@@ -53,6 +59,18 @@ export function LoginPage() {
|
|||||||
setError(err instanceof Error ? err.message : "Login gagal"),
|
setError(err instanceof Error ? err.message : "Login gagal"),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Show loading spinner while OAuth token is being processed
|
||||||
|
if (oauthProcessing) {
|
||||||
|
return (
|
||||||
|
<main className="flex min-h-screen items-center justify-center px-6 py-12">
|
||||||
|
<div className="flex flex-col items-center gap-3">
|
||||||
|
<div className="h-10 w-10 border-4 border-green-500 border-t-transparent rounded-full animate-spin" />
|
||||||
|
<p className="text-gray-500 text-sm">Menyelesaikan login dengan Google...</p>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="flex min-h-screen items-center justify-center px-6 py-12">
|
<main className="flex min-h-screen items-center justify-center px-6 py-12">
|
||||||
<div className="w-full max-w-sm md:max-w-md space-y-4">
|
<div className="w-full max-w-sm md:max-w-md space-y-4">
|
||||||
|
|||||||
Reference in New Issue
Block a user