Skip to content

Commit 2668141

Browse files
committed
feat(getcloser): redirect a page based on progress status
1 parent 81b1c1e commit 2668141

5 files changed

Lines changed: 158 additions & 171 deletions

File tree

getcloser/frontend/src/app/pages/Page1.tsx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,9 @@ import { Button } from '@/components/ui/button';
55
import { Input } from '@/components/ui/input';
66
import { Label } from '@/components/ui/label';
77
import { useFormStore } from '../../store/formStore';
8-
import { useNavigationStore } from '../../store/navigationStore';
98

109
export default function Page1() {
1110
const { email, setEmail, setId, setAccessToken, setTeamId, setChallengeId, setProgressStatus } = useFormStore();
12-
const { setCurrentPage } = useNavigationStore();
1311

1412
const handleSubmit = async (e: React.FormEvent) => {
1513
e.preventDefault();
@@ -63,8 +61,6 @@ export default function Page1() {
6361
if (userMeResult.progress_status) {
6462
setProgressStatus(userMeResult.progress_status);
6563
}
66-
alert('정보가 제출되었습니다!');
67-
setCurrentPage('page2');
6864
} catch (error) {
6965
console.error('Error submitting form:', error);
7066
alert('정보 제출에 실패했습니다.');

getcloser/frontend/src/app/pages/Page2.tsx

Lines changed: 21 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ const CreateTeamView = ({
142142
};
143143

144144
export default function Page2() {
145-
const { id: myId, teamId, setTeamId, setMemberIds, reset } = useFormStore();
145+
const { id: myId, teamId, setTeamId, setMemberIds, progressStatus } = useFormStore();
146146
const { setCurrentPage } = useNavigationStore();
147147

148148
const [view, setView] = useState<View>('loading');
@@ -184,83 +184,33 @@ export default function Page2() {
184184

185185
useEffect(() => {
186186
const initialize = async () => {
187-
if (!myId) {
188-
setCurrentPage('page1');
189-
return;
190-
}
191-
if (view !== 'loading') return;
192-
193-
try {
194-
const response = await authenticatedFetch('/api/v1/teams/me');
195-
196-
if (response.status === 404) {
197-
setView('create');
198-
return;
199-
}
200-
201-
if (!response.ok) {
202-
throw new Error(`HTTP error! status: ${response.status}, message: ${(await response.json()).detail || response.statusText}`);
203-
}
204-
205-
const teamData = await response.json();
206-
207-
if (teamData && teamData.team_id) {
208-
if (teamData.status === 'ACTIVE') {
209-
setCurrentPage('page3');
210-
return;
211-
}
212-
213-
if (teamData.status === 'PENDING') {
214-
setTeamId(teamData.team_id);
215-
if (teamData.members && teamData.members.length > 0) {
216-
const me = teamData.members.find((m: {id: number}) => Number(m.id) === Number(myId));
217-
const others = teamData.members.filter((m: {id: number}) => Number(m.id) !== Number(myId));
218-
const newInputs: InputState[] = Array(TEAM_SIZE).fill({ id: '', displayName: '' });
219-
220-
if (me) {
221-
newInputs[0] = { id: String(me.id), displayName: me.name };
222-
} else {
223-
newInputs[0] = { id: String(myId), displayName: '' }; // Fallback
224-
}
225-
226-
others.forEach((member: { id: number; name: string }, i: number) => {
227-
if (i + 1 < TEAM_SIZE) {
228-
newInputs[i + 1] = { id: String(member.id), displayName: member.name };
229-
}
230-
});
231-
232-
const initialTeamMembers: TeamMember[] = teamData.members.map((member: { id: number; name: string; }) => ({
233-
user_id: Number(member.id),
234-
displayName: member.name,
235-
is_ready: false,
236-
}));
237-
238-
setInputs(newInputs);
239-
setTeamMembers(initialTeamMembers);
240-
setView('waiting');
241-
} else {
242-
setView('create');
243-
}
244-
return;
245-
}
246-
}
187+
if (progressStatus === 'NONE_TEAM') {
247188
setView('create');
248-
} catch (error) {
249-
console.error('Error during page initialization:', error);
250-
if (teamId && teamId > 0) {
251-
try {
252-
await authenticatedFetch(`/api/v1/teams/${teamId}/cancel`, { method: 'POST' });
253-
} catch (cancelError) {
254-
console.error(`Failed to leave team ${teamId} after init error:`, cancelError);
189+
} else if (progressStatus === 'TEAM_WAITING') {
190+
try {
191+
const response = await authenticatedFetch('/api/v1/teams/me');
192+
if (!response.ok) {
193+
throw new Error('Failed to fetch team data');
255194
}
195+
const teamData = await response.json();
196+
if (teamData && teamData.members) {
197+
const initialTeamMembers: TeamMember[] = teamData.members.map((member: { id: number; name: string; }) => ({
198+
user_id: Number(member.id),
199+
displayName: member.name,
200+
is_ready: false,
201+
}));
202+
setTeamMembers(initialTeamMembers);
203+
}
204+
setView('waiting');
205+
} catch (error) {
206+
console.error('Error fetching team data for waiting view:', error);
207+
setView('create'); // Fallback to create view
256208
}
257-
reset();
258-
setView('create');
259209
}
260210
};
261211

262212
initialize();
263-
}, [myId, view, setCurrentPage, setTeamId, setInputs, setTeamMembers, reset]);
213+
}, [progressStatus]);
264214

265215
useEffect(() => {
266216
if (myId && inputs[0].id === '') {

getcloser/frontend/src/app/pages/Page3.tsx

Lines changed: 29 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,18 @@ export default function Page3() {
4040
const { setCurrentPage } = useNavigationStore();
4141

4242
useEffect(() => {
43-
const CHALLENGE_DATA_KEY = 'challengeData';
44-
4543
const initializeChallenge = async () => {
46-
if (!question && id && teamId) {
44+
// If a question is already loaded, do nothing.
45+
if (question) {
46+
console.log('Question already exists, skipping initialization.');
47+
return;
48+
}
49+
50+
// If we are on page3, we should have the necessary IDs.
51+
// If not, something is wrong, but providers.tsx should redirect.
52+
if (id && teamId) {
53+
console.log('No question found in store. Fetching/assigning challenge from server...');
54+
4755
// Fetch team members to get their names
4856
let members: TeamMember[] = [];
4957
try {
@@ -68,30 +76,9 @@ export default function Page3() {
6876
return member ? member.name : String(userId); // Fallback to user_id as string
6977
};
7078

71-
// 1. Try to restore challenge from localStorage
72-
const savedChallengeJSON = localStorage.getItem(CHALLENGE_DATA_KEY);
73-
if (savedChallengeJSON) {
74-
console.log('Restoring challenge from localStorage...');
75-
try {
76-
const savedChallenge = JSON.parse(savedChallengeJSON);
77-
if (savedChallenge.category && savedChallenge.assigned_challenge_id) {
78-
const questionInfo = questions.find((q) => q.category === String(savedChallenge.category));
79-
if (questionInfo) {
80-
const memberName = findMemberName(savedChallenge.user_id);
81-
setQuestion([memberName, questionInfo.problem].join(' '));
82-
setChallengeId(savedChallenge.assigned_challenge_id);
83-
return; // Challenge successfully restored
84-
}
85-
}
86-
} catch (e) {
87-
console.error('Failed to parse challenge data from localStorage', e);
88-
localStorage.removeItem(CHALLENGE_DATA_KEY); // Clear corrupted data
89-
}
90-
}
91-
92-
// 2. If no valid saved data, assign a new challenge
93-
console.log('No valid saved challenge found. Assigning a new one...');
9479
try {
80+
// This endpoint will assign a new challenge if one doesn't exist,
81+
// or it should ideally return the existing one.
9582
const assignResponse = await authenticatedFetch('/api/v1/challenges/assign', {
9683
method: 'POST',
9784
headers: { 'Content-Type': 'application/json' },
@@ -100,63 +87,40 @@ export default function Page3() {
10087

10188
if (!assignResponse.ok) {
10289
const errorData = await getJsonFromResponse(assignResponse);
103-
throw new Error(`Failed to assign challenge: ${errorData.detail}`);
90+
throw new Error(`Failed to assign/fetch challenge: ${errorData.detail}`);
10491
}
10592

10693
const newChallengeData = await assignResponse.json();
107-
console.log('Successfully assigned new challenge:', newChallengeData);
94+
console.log('Successfully assigned/fetched challenge:', newChallengeData);
10895

10996
if (newChallengeData.my_assigned && newChallengeData.my_assigned.category && newChallengeData.my_assigned.assigned_challenge_id) {
110-
const dataToSave = {
111-
category: String(newChallengeData.my_assigned.category),
112-
assigned_challenge_id: newChallengeData.my_assigned.assigned_challenge_id,
113-
user_id: newChallengeData.my_assigned.user_id,
114-
};
115-
116-
// Save to localStorage for future restoration
117-
localStorage.setItem(CHALLENGE_DATA_KEY, JSON.stringify(dataToSave));
118-
console.log('Saved new challenge to localStorage.');
119-
120-
// Set state from the new challenge data
121-
const questionInfo = questions.find((q) => q.category === dataToSave.category);
97+
const { user_id: userId, category, assigned_challenge_id } = newChallengeData.my_assigned;
98+
99+
// Set state from the challenge data
100+
const questionInfo = questions.find((q) => q.category === String(category));
122101
if (questionInfo) {
123-
const memberName = findMemberName(dataToSave.user_id);
102+
const memberName = findMemberName(userId);
124103
setQuestion([memberName, questionInfo.problem].join(' '));
125-
setChallengeId(dataToSave.assigned_challenge_id);
104+
setChallengeId(assigned_challenge_id);
105+
} else {
106+
throw new Error(`Could not find question for category: ${category}`);
126107
}
127108
} else {
128109
throw new Error('Assigned challenge data is incomplete or malformed.');
129110
}
130111
} catch (error) {
131-
console.error('Error assigning challenge:', error);
132-
// General error handling: leave team, reset state, clear local storage
133-
if (teamId && teamId > 0) {
134-
try {
135-
console.log(`Attempting to leave team ${teamId} due to error...`);
136-
await authenticatedFetch(`/api/v1/teams/${teamId}/cancel`, { method: 'POST' });
137-
console.log(`Successfully left team ${teamId}.`);
138-
} catch (cancelError) {
139-
console.error(`Failed to leave team ${teamId}:`, cancelError);
140-
}
141-
}
142-
localStorage.removeItem(CHALLENGE_DATA_KEY);
112+
console.error('Error in challenge initialization:', error);
113+
// Attempt to recover by going back to team formation
143114
reset();
144-
145-
setCurrentPage('page1');
115+
setCurrentPage('page2');
146116
}
147-
} else if (question) {
148-
console.log('Question already exists, skipping initialization.');
149-
} else {
150-
console.log('Missing id or teamId, cannot initialize challenge. Resetting.');
151-
localStorage.removeItem(CHALLENGE_DATA_KEY);
152-
reset();
153-
154-
setCurrentPage('page1');
155117
}
156118
};
157119

158120
initializeChallenge();
159-
}, [id, teamId, question, memberIds, setQuestion, setChallengeId, reset, setCurrentPage]);
121+
// Added challengeId to dependency array to react to changes if needed,
122+
// though the main trigger is the absence of `question`.
123+
}, [id, teamId, question, memberIds, setQuestion, setChallengeId, reset, setCurrentPage, challengeId]);
160124

161125
const handleSubmit = async (e: React.FormEvent) => {
162126
e.preventDefault();

getcloser/frontend/src/app/pages/Page4.tsx

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ interface ChallengeResult {
3434
}
3535

3636
export default function Page4() {
37-
const { id, isCorrect } = useFormStore(); // Added isCorrect
37+
const { id, progressStatus } = useFormStore(); // Use progressStatus
3838
const { setCurrentPage } = useNavigationStore();
3939

4040
const [result, setResult] = useState<string>('');
@@ -44,15 +44,17 @@ export default function Page4() {
4444
const [selectedMemberChallenge, setSelectedMemberChallenge] = useState<ChallengeResult | null>(null);
4545
const [isModalOpen, setIsModalOpen] = useState(false);
4646

47-
// This useEffect will set the initial success/failure based on isCorrect from the store
47+
// This useEffect will set the initial success/failure based on progressStatus from the store
4848
useEffect(() => {
49-
if (isCorrect === null) {
50-
console.warn('isCorrect is not set, defaulting to 실패');
49+
if (progressStatus === 'CHALLENGE_SUCCESS' || progressStatus === 'REDEEMED') {
50+
setResult('성공');
51+
} else if (progressStatus === 'CHALLENGE_FAILED') {
5152
setResult('실패');
5253
} else {
53-
setResult(isCorrect ? '성공' : '실패');
54+
console.warn('progressStatus is not set to a known success/failure state, defaulting to 실패');
55+
setResult('실패');
5456
}
55-
}, [isCorrect]); // Depend on isCorrect from the store
57+
}, [progressStatus]); // Depend on progressStatus from the store
5658

5759
// New useEffect to fetch team data when result is '성공'
5860
useEffect(() => {
@@ -166,21 +168,24 @@ export default function Page4() {
166168
<div className="mt-8 text-left">
167169
<h3 className="text-2xl font-bold mb-4">우리 팀원들</h3>
168170
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
169-
{teamData.members.map((member) => (
170-
<div key={member.user_id} className="bg-muted p-4 rounded-lg shadow-md cursor-pointer hover:bg-muted/80" onClick={() => handleMemberClick(member.user_id)}>
171-
<p className="text-lg font-semibold">{member.name}</p>
172-
{member.github_url && (
173-
<p className="text-sm text-gray-600">
171+
{teamData.members
172+
.filter(member => member.id !== id) // Filter out current user's info
173+
.map((member) => (
174+
<div key={member.id} className="bg-muted p-4 rounded-lg shadow-md">
175+
<p className="text-lg font-semibold">{member.name}</p>
176+
<p className="text-sm text-gray-600">Email: {member.email}</p>
177+
{member.github_url && (
178+
<p className="text-sm text-gray-600">
174179
GitHub: <a href={member.github_url} target="_blank" rel="noopener noreferrer" className="text-blue-500 hover:underline">{member.github_url}</a>
175-
</p>
176-
)}
177-
{member.linkedin_url && (
178-
<p className="text-sm text-gray-600">
180+
</p>
181+
)}
182+
{member.linkedin_url && (
183+
<p className="text-sm text-gray-600">
179184
LinkedIn: <a href={member.linkedin_url} target="_blank" rel="noopener noreferrer" className="text-blue-500 hover:underline">{member.linkedin_url}</a>
180-
</p>
181-
)}
182-
</div>
183-
))}
185+
</p>
186+
)}
187+
</div>
188+
))}
184189
</div>
185190
</div>
186191
)}

0 commit comments

Comments
 (0)