diff --git a/frontend/src/components/common/LoadingScreen.tsx b/frontend/src/components/common/LoadingScreen.tsx
new file mode 100644
index 0000000..86fe71c
--- /dev/null
+++ b/frontend/src/components/common/LoadingScreen.tsx
@@ -0,0 +1,192 @@
+/* eslint-disable react-hooks/purity */
+'use client';
+
+import React, { useEffect, useState, useMemo } from 'react';
+import { motion } from 'framer-motion';
+import Image from 'next/image';
+
+interface LoadingScreenProps {
+ message?: string;
+}
+
+const MBTI_GROUPS = [
+ {
+ name: 'Analysts',
+ color: '#E0D7FF', // Purple-ish
+ textColor: '#5D2FB7',
+ characters: ['INTJ', 'INTP', 'ENTJ', 'ENTP'],
+ },
+ {
+ name: 'Diplomats',
+ color: '#D7FFD7', // Green-ish
+ textColor: '#2D812D',
+ characters: ['INFJ', 'INFP', 'ENFJ', 'ENFP'],
+ },
+ {
+ name: 'Sentinels',
+ color: '#D7F3FF', // Blue-ish
+ textColor: '#2B6DA1',
+ characters: ['ISTJ', 'ISFJ', 'ESTJ', 'ESFJ'],
+ },
+ {
+ name: 'Explorers',
+ color: '#FFF7D7', // Yellow-ish
+ textColor: '#A17D1F',
+ characters: ['ISTP', 'ISFP', 'ESTP', 'ESFP'],
+ },
+];
+
+export default function LoadingScreen({
+ message = 'Loading...',
+}: LoadingScreenProps) {
+ const [activeStep, setActiveStep] = useState(0);
+
+ // Pick one random character from each group
+ const selectedCharacters = useMemo(() => {
+ return MBTI_GROUPS.map((group) => {
+ const randomIndex = Math.floor(Math.random() * group.characters.length);
+ return {
+ id: group.characters[randomIndex],
+ groupColor: group.color,
+ textColor: group.textColor,
+ };
+ });
+ }, []);
+
+ useEffect(() => {
+ const timer = setInterval(() => {
+ setActiveStep((prev) => (prev + 1) % selectedCharacters.length);
+ }, 1500); // Wait for jump animation to mostly complete
+
+ return () => clearInterval(timer);
+ }, [selectedCharacters.length]);
+
+ return (
+
+ {/* Retro-pop dot pattern background */}
+
+
+
+ {/* Characters Row */}
+
+ {selectedCharacters.map((char, index) => {
+ const isActive = index === activeStep;
+
+ return (
+
+
+
+
+
+ {/* Visual indicator / shadow under active char */}
+
+
+ );
+ })}
+
+
+ {/* Loading Text */}
+
+
+ {message}
+
+
+ {/* Pulsing dots indicator */}
+
+ {[0, 1, 2].map((i) => (
+
+ ))}
+
+
+
+
+ {/* Group Name display (Subtle) */}
+
+
+ {MBTI_GROUPS[activeStep].name}
+
+
+
+ );
+}
diff --git a/frontend/src/features/diagnosis/components/BaselineSurvey.tsx b/frontend/src/features/diagnosis/components/BaselineSurvey.tsx
index 0e8a1a4..12e7825 100644
--- a/frontend/src/features/diagnosis/components/BaselineSurvey.tsx
+++ b/frontend/src/features/diagnosis/components/BaselineSurvey.tsx
@@ -11,7 +11,7 @@ import {
type BaselineAnswers,
type AnswerOption,
} from '@/features/diagnosis/types';
-import Spinner from '@/components/ui/Spinner';
+import LoadingScreen from '@/components/common/LoadingScreen';
import { postRegister, submitGame } from '@/lib/api';
type Status = 'answering' | 'loading' | 'error' | 'success';
@@ -79,11 +79,7 @@ export default function BaselineSurvey() {
};
if (status === 'loading') {
- return (
-
-
-
- );
+ return ;
}
if (status === 'error') {
@@ -101,19 +97,7 @@ export default function BaselineSurvey() {
}
if (status === 'success') {
- return (
-
-
診断完了!
-
- これからゲームが始まります。
-
- ゲームでのあなたの行動から、本当の性格を分析します。
-
-
- まもなくゲーム画面に移動します...
-
-
- );
+ return ;
}
return (
diff --git a/frontend/src/features/games/terms/components/TermsGameFlow.tsx b/frontend/src/features/games/terms/components/TermsGameFlow.tsx
index 4a029bc..b163dab 100644
--- a/frontend/src/features/games/terms/components/TermsGameFlow.tsx
+++ b/frontend/src/features/games/terms/components/TermsGameFlow.tsx
@@ -7,6 +7,7 @@ import type { Game1Data, ScrollEvent } from '@/features/games/types';
import { game1DataAtom } from '@/stores/games';
import PopupAd from './PopupAd';
import PopupTerms from './PopupTerms';
+import LoadingScreen from '@/components/common/LoadingScreen';
// TODO: バックエンド接続時にコメントアウトを解除する
// import { submitGame } from '@/lib/api';
@@ -183,16 +184,7 @@ export default function TermsGameFlow() {
);
if (isCompleted) {
- return (
-
-
-
完了しました
-
- 次のゲームに移動します...
-
-
-
- );
+ return ;
}
return (
diff --git a/frontend/src/features/result/components/ResultPage.tsx b/frontend/src/features/result/components/ResultPage.tsx
index 96c708a..2179b0b 100644
--- a/frontend/src/features/result/components/ResultPage.tsx
+++ b/frontend/src/features/result/components/ResultPage.tsx
@@ -5,13 +5,14 @@ import { resultAtom } from '@/stores/result';
import { useResult } from '../hooks/useResult';
import AnalyzingView from './AnalyzingView';
import ResultReport from './ResultReport';
+import LoadingScreen from '@/components/common/LoadingScreen';
export default function ResultPage() {
const { status, errorMessage, retry } = useResult();
const result = useAtomValue(resultAtom);
if (status === 'loading') {
- return ;
+ return ;
}
if (status === 'error') {
@@ -28,5 +29,5 @@ export default function ResultPage() {
return ;
}
- return ;
+ return ;
}