Spaces:
Sleeping
Sleeping
| 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 user is logged in, fetch their count | |
| 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); | |
| } | |
| }; | |
| // Loading state | |
| 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> | |
| ); | |
| } | |
| // Not logged in state | |
| 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; | |