Skip to content

Commit 7d7b3cf

Browse files
authored
Merge pull request #107 from kc3hack/feature/issue-102-rank-charge-animation
feat: ランク測定の長押しチャージUI (#102)
2 parents 3df4e38 + 8266c62 commit 7d7b3cf

File tree

5 files changed

+679
-4
lines changed

5 files changed

+679
-4
lines changed

frontend/src/app/globals.css

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,72 @@ body {
4545
80% { transform: translateY(-8px); }
4646
90% { transform: translateY(-4px); }
4747
100% { transform: translateY(0px); }
48+
}
49+
50+
@keyframes fadeIn {
51+
from {
52+
opacity: 0;
53+
transform: scale(0.8);
54+
}
55+
to {
56+
opacity: 1;
57+
transform: scale(1);
58+
}
59+
}
60+
61+
@keyframes scaleIn {
62+
0% {
63+
transform: scale(0);
64+
}
65+
50% {
66+
transform: scale(1.1);
67+
}
68+
100% {
69+
transform: scale(1);
70+
}
71+
}
72+
73+
@keyframes glow {
74+
0%, 100% {
75+
filter: drop-shadow(0 0 10px rgba(252, 211, 77, 0.5));
76+
}
77+
50% {
78+
filter: drop-shadow(0 0 20px rgba(252, 211, 77, 0.9));
79+
}
80+
}
81+
82+
@keyframes sparkle {
83+
0%, 100% {
84+
opacity: 0;
85+
transform: scale(0) rotate(0deg);
86+
}
87+
50% {
88+
opacity: 1;
89+
transform: scale(1.5) rotate(180deg);
90+
}
91+
}
92+
93+
@keyframes shimmer {
94+
0% {
95+
transform: translateX(-100%);
96+
}
97+
100% {
98+
transform: translateX(100%);
99+
}
100+
}
101+
102+
.animate-fadeIn {
103+
animation: fadeIn 1s ease-out forwards;
104+
}
105+
106+
.animate-scaleIn {
107+
animation: scaleIn 0.8s ease-out forwards;
108+
}
109+
110+
.animate-glow {
111+
animation: glow 2s ease-in-out infinite;
112+
}
113+
114+
.animate-sparkle {
115+
animation: sparkle 1.5s ease-in-out infinite;
48116
}

frontend/src/app/login/page.tsx

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { useState } from "react";
44
import { useRouter } from "next/navigation";
55
import { login, register } from "@/lib/api/auth";
6+
import { RankMeasurement } from "@/features/dashboard/components/RankMeasurement";
67

78
export default function LoginPage() {
89
const router = useRouter();
@@ -11,6 +12,7 @@ export default function LoginPage() {
1112
const [isRegister, setIsRegister] = useState(false);
1213
const [error, setError] = useState<string | null>(null);
1314
const [loading, setLoading] = useState(false);
15+
const [showRankMeasurement, setShowRankMeasurement] = useState(false);
1416

1517
const handleSubmit = async (e: React.FormEvent) => {
1618
e.preventDefault();
@@ -33,14 +35,19 @@ export default function LoginPage() {
3335
};
3436

3537
const handleGitHubLogin = () => {
36-
// GitHub OAuth フローを開始(バックエンドへリダイレクト)
38+
// ランク測定UIを表示
39+
setShowRankMeasurement(true);
40+
};
41+
42+
const handleRankMeasurementComplete = () => {
43+
// ランク測定完了後、GitHub OAuth フローを開始
3744
const apiBaseUrl =
3845
process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000";
3946
window.location.href = `${apiBaseUrl}/api/v1/auth/github/login`;
4047
};
4148

4249
return (
43-
<div className="flex min-h-screen items-center justify-center bg-gradient-to-br from-[#97C88C] via-[#A8D5A1] to-[#8BC880] p-4">
50+
<div className="flex min-h-screen items-center justify-center bg-linear-to-br from-[#97C88C] via-[#A8D5A1] to-[#8BC880] p-4">
4451
<div className="w-full max-w-md rounded-lg border-4 border-[#2C5F2D] bg-[#F5F5DC] p-8 shadow-[8px_8px_0_0_#2C5F2D] animate-[slideUp_0.3s_ease-out]">
4552
{/* Header with Icon */}
4653
<div className="mb-6 text-center">
@@ -65,12 +72,12 @@ export default function LoginPage() {
6572
type="button"
6673
onClick={handleGitHubLogin}
6774
disabled={loading}
68-
className="group relative w-full overflow-hidden rounded border-2 border-[#2C5F2D] bg-[#2C5F2D] p-4 font-mono font-bold tracking-widest text-white shadow-[4px_4px_0_0_#1F4521] transition-all hover:translate-x-[2px] hover:translate-y-[2px] hover:shadow-[2px_2px_0_0_#1F4521] active:translate-x-[4px] active:translate-y-[4px] active:shadow-none disabled:opacity-50 disabled:cursor-not-allowed"
75+
className="group relative w-full overflow-hidden rounded border-2 border-[#2C5F2D] bg-[#2C5F2D] p-4 font-mono font-bold tracking-widest text-white shadow-[4px_4px_0_0_#1F4521] transition-all hover:translate-x-0.5 hover:translate-y-0.5 hover:shadow-[2px_2px_0_0_#1F4521] active:translate-x-1 active:translate-y-1 active:shadow-none disabled:opacity-50 disabled:cursor-not-allowed"
6976
>
7077
<span className="relative z-10 flex items-center justify-center gap-2">
7178
🚀 GitHub でログイン(推奨)
7279
</span>
73-
<div className="absolute inset-0 -translate-x-full bg-gradient-to-r from-transparent via-white/20 to-transparent group-hover:translate-x-full transition-transform duration-700" />
80+
<div className="absolute inset-0 -translate-x-full bg-linear-to-r from-transparent via-white/20 to-transparent group-hover:translate-x-full transition-transform duration-700" />
7481
</button>
7582
<p className="mt-3 text-center text-xs text-gray-600 leading-relaxed">
7683
💡 GitHubリポジトリを分析して
@@ -184,6 +191,17 @@ export default function LoginPage() {
184191
}
185192
}
186193
`}</style>
194+
195+
{/* ランク測定モーダル */}
196+
{showRankMeasurement && (
197+
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm animate-fadeIn">
198+
<div className="w-full h-full overflow-auto">
199+
<RankMeasurement
200+
onComplete={handleRankMeasurementComplete}
201+
/>
202+
</div>
203+
</div>
204+
)}
187205
</div>
188206
);
189207
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
"use client";
2+
3+
/**
4+
* Rank Measurement Page
5+
* ランク測定専用ページ
6+
*/
7+
8+
import { withAuth } from "@/lib/auth/withAuth";
9+
import { RankMeasurement } from "@/features/dashboard/components/RankMeasurement";
10+
import { useRouter } from "next/navigation";
11+
12+
function RankMeasurementPage() {
13+
const router = useRouter();
14+
15+
const handleComplete = () => {
16+
// ランク測定完了後、ダッシュボードに戻る
17+
router.push('/dashboard');
18+
};
19+
20+
return (
21+
<div className="min-h-screen bg-linear-to-b from-[#FDFEF0] to-[#E8F5E9] flex flex-col">
22+
{/* ヘッダー */}
23+
<header className="sticky top-0 z-50 flex h-16 w-full items-center justify-between bg-[#14532D] px-6 text-white border-b-4 border-black shadow-[0_4px_0_rgba(0,0,0,0.2)]">
24+
<div className="flex items-center gap-2">
25+
<div className="h-8 w-8 bg-[#FCD34D] border-2 border-black animate-pulse"></div>
26+
<span className="font-sans font-bold text-xl tracking-widest text-[#FCD34D] [text-shadow:2px_2px_0_black]">
27+
KC3 HACK
28+
</span>
29+
</div>
30+
<button
31+
onClick={() => router.push('/dashboard')}
32+
className="px-4 py-2 text-sm font-bold bg-[#4ADE80] text-[#14532D] border-2 border-black hover:translate-y-1 transition-all"
33+
>
34+
ダッシュボードに戻る
35+
</button>
36+
</header>
37+
38+
{/* メインコンテンツ */}
39+
<main className="flex-1 flex items-center justify-center">
40+
<RankMeasurement onComplete={handleComplete} />
41+
</main>
42+
</div>
43+
);
44+
}
45+
46+
export default withAuth(RankMeasurementPage);

frontend/src/features/dashboard/components/AppSidebar.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,17 @@ export function AppSidebar() {
6464
<span className="text-lg">STATS</span>
6565
</Link>
6666
</li>
67+
68+
{/* Rank Measurement */}
69+
<li>
70+
<Link
71+
href="/rank-measurement"
72+
className={getLinkClass('/rank-measurement')}
73+
>
74+
<div className="flex h-8 w-8 items-center justify-center text-xl mr-3 bg-white border-2 border-black text-black">🎯</div>
75+
<span className="text-lg">RANK</span>
76+
</Link>
77+
</li>
6778
</ul>
6879

6980
{/* System Info Box */}

0 commit comments

Comments
 (0)