|
import React, { useState, useEffect } from "react"; |
|
import { apiFetch } from "../utils/apiFetch"; |
|
|
|
interface UserInfo { |
|
connected: boolean; |
|
username: string | null; |
|
} |
|
|
|
interface CountResponse { |
|
name: string; |
|
count: number; |
|
} |
|
|
|
const Counter: React.FC = () => { |
|
const [userInfo, setUserInfo] = useState<UserInfo | null>(null); |
|
const [count, setCount] = useState<number>(0); |
|
const [loading, setLoading] = useState(true); |
|
const [error, setError] = useState<string | null>(null); |
|
const [incrementing, setIncrementing] = useState(false); |
|
|
|
useEffect(() => { |
|
const fetchUserInfo = async () => { |
|
try { |
|
setLoading(true); |
|
const response = await apiFetch("/api/user"); |
|
if (!response.ok) { |
|
throw new Error(`HTTP error! status: ${response.status}`); |
|
} |
|
const data: UserInfo = await response.json(); |
|
setUserInfo(data); |
|
setError(null); |
|
|
|
|
|
if (data.connected) { |
|
await fetchUserCount(); |
|
} |
|
} catch (err) { |
|
setError( |
|
err instanceof Error ? err.message : "Failed to fetch user info", |
|
); |
|
setUserInfo(null); |
|
} finally { |
|
setLoading(false); |
|
} |
|
}; |
|
|
|
fetchUserInfo(); |
|
}, []); |
|
|
|
const fetchUserCount = async () => { |
|
try { |
|
const response = await apiFetch("/api/user/count"); |
|
if (!response.ok) { |
|
throw new Error(`HTTP error! status: ${response.status}`); |
|
} |
|
const data: CountResponse = await response.json(); |
|
setCount(data.count); |
|
} catch (err) { |
|
console.error("Failed to fetch user count:", err); |
|
setCount(0); |
|
} |
|
}; |
|
|
|
const handleIncrement = async () => { |
|
if (!userInfo?.connected) { |
|
setError("You must be logged in to increment the counter"); |
|
return; |
|
} |
|
|
|
try { |
|
setIncrementing(true); |
|
setError(null); |
|
|
|
const response = await apiFetch("/api/user/count/increment", { |
|
method: "POST", |
|
}); |
|
|
|
if (!response.ok) { |
|
throw new Error(`HTTP error! status: ${response.status}`); |
|
} |
|
|
|
const data: CountResponse = await response.json(); |
|
setCount(data.count); |
|
} catch (err) { |
|
setError( |
|
err instanceof Error ? err.message : "Failed to increment counter", |
|
); |
|
} finally { |
|
setIncrementing(false); |
|
} |
|
}; |
|
|
|
|
|
if (loading) { |
|
return ( |
|
<div className="flex items-center justify-center"> |
|
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-blue-500"></div> |
|
<span className="ml-2 text-gray-400 text-base">Loading...</span> |
|
</div> |
|
); |
|
} |
|
|
|
|
|
if (!userInfo?.connected) { |
|
return ( |
|
<div className="text-center"> |
|
<p className="text-gray-400 text-base mb-4"> |
|
Please log in to use the counter |
|
</p> |
|
</div> |
|
); |
|
} |
|
|
|
return ( |
|
<div className="space-y-4"> |
|
{error && ( |
|
<div className="p-3 bg-red-900/20 border border-red-700 rounded-lg"> |
|
<p className="text-red-400 text-base">{error}</p> |
|
</div> |
|
)} |
|
|
|
<button |
|
onClick={handleIncrement} |
|
disabled={incrementing} |
|
className="w-full bg-gradient-to-r from-blue-600 to-blue-700 hover:from-blue-700 hover:to-blue-800 disabled:from-gray-600 disabled:to-gray-700 text-white font-semibold py-4 px-8 rounded-xl border border-transparent hover:border-blue-400 transition-all duration-300 focus:outline-none focus:ring-4 focus:ring-blue-500/50 shadow-lg hover:shadow-xl transform hover:-translate-y-1 disabled:transform-none disabled:hover:shadow-lg relative" |
|
> |
|
<div |
|
className={`flex items-center justify-center space-x-2 transition-opacity duration-200 ${incrementing ? "opacity-0" : "opacity-100"}`} |
|
> |
|
Count is {count} |
|
</div> |
|
|
|
<div |
|
className={`absolute inset-0 flex items-center justify-center space-x-2 transition-opacity duration-200 ${incrementing ? "opacity-100" : "opacity-0"}`} |
|
> |
|
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white"></div> |
|
<span>Incrementing...</span> |
|
</div> |
|
</button> |
|
|
|
<p className="text-gray-400 text-sm"> |
|
Logged in as: {userInfo.username || "User"} |
|
</p> |
|
</div> |
|
); |
|
}; |
|
|
|
export default Counter; |
|
|