File size: 4,380 Bytes
e92d5c0 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
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;
|