Skip to content

Commit f0927c2

Browse files
authored
Merge pull request #672 from Kaylahray/frontend-workspace
feat(frontend): wire Next.js app to backend auth, puzzles, and dashboard
2 parents 39bac1b + cb371ba commit f0927c2

24 files changed

Lines changed: 609 additions & 476 deletions

File tree

frontend/app/auth/signin/page.tsx

Lines changed: 52 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ import ErrorBoundary from '@/components/error/ErrorBoundary';
1111
import { useToast } from '@/components/ui/ToastProvider';
1212
import { useStellarWalletAuth } from '@/hooks/useStellarWalletAuth';
1313
import { useAuth } from '@/hooks/useAuth';
14+
import { useGoogleAuth } from '@/hooks/useGoogleAuth';
15+
import { useGuestSession } from '@/hooks/useGuestSession';
16+
import { signIn } from '@/lib/api/authApi';
1417
import { WalletType } from '@/lib/stellar/types';
1518
import WalletModal, { WalletType as ModalWalletType } from '@/components/ui/WalletModal';
1619
import { useWalletModal } from '@/hooks/useWalletModal';
@@ -26,6 +29,8 @@ const SignInPage = () => {
2629
clearError,
2730
} = useStellarWalletAuth();
2831
const { loginSuccess, loginFailure, setLoading } = useAuth();
32+
const { signInWithGoogle } = useGoogleAuth();
33+
const { startGuestSession, isStarting: isGuestStarting } = useGuestSession();
2934
const { isOpen: isWalletModalOpen, openModal: openWalletModal, closeModal: closeWalletModal } = useWalletModal();
3035
const [formData, setFormData] = useState({
3136
username: '',
@@ -78,123 +83,59 @@ const SignInPage = () => {
7883
}
7984

8085
try {
81-
const response = await fetch('http://localhost:3000/auth/signIn', {
82-
method: 'POST',
83-
headers: {
84-
'Content-Type': 'application/json',
85-
},
86-
body: JSON.stringify({
87-
email: formData.username,
88-
password: formData.password
89-
}),
90-
});
91-
92-
// Checking if response is ok before trying to parse JSON
93-
if (!response.ok) {
94-
try {
95-
const errorData = await response.json();
96-
// Handle specific error messages from server
97-
if (response.status === 401) {
98-
const errorMsg = 'Invalid email or password.';
99-
showError('Login Failed', errorMsg);
100-
loginFailure(errorMsg);
101-
setIsLoading(false);
102-
setLoading(false);
103-
} else if (response.status === 404) {
104-
const errorMsg = 'Account not found.';
105-
showError('Account Not Found', errorMsg);
106-
loginFailure(errorMsg);
107-
setIsLoading(false);
108-
setLoading(false);
109-
} else if (response.status === 400) {
110-
const errorMsg = errorData.message || 'Invalid input.';
111-
showError('Invalid Input', errorMsg);
112-
loginFailure(errorMsg);
113-
setIsLoading(false);
114-
setLoading(false);
115-
} else if (response.status >= 500) {
116-
const errorMsg = 'Server error. Please try again later.';
117-
showError('Server Error', errorMsg);
118-
loginFailure(errorMsg);
119-
setIsLoading(false);
120-
setLoading(false);
121-
} else {
122-
const errorMsg = errorData.message || 'Login failed. Please try again.';
123-
showError('Login Failed', errorMsg);
124-
loginFailure(errorMsg);
125-
setIsLoading(false);
126-
setLoading(false);
127-
}
128-
} catch {
129-
// If response isn't JSON, use status text or default message
130-
if (response.status === 401) {
131-
const errorMsg = 'Invalid email or password.';
132-
showError('Login Failed', errorMsg);
133-
loginFailure(errorMsg);
134-
setIsLoading(false);
135-
setLoading(false);
136-
} else if (response.status === 404) {
137-
const errorMsg = 'Account not found.';
138-
showError('Account Not Found', errorMsg);
139-
loginFailure(errorMsg);
140-
setIsLoading(false);
141-
setLoading(false);
142-
} else {
143-
const errorMsg = `Login failed: ${response.statusText || 'Please try again.'}`;
144-
showError('Login Failed', errorMsg);
145-
loginFailure(errorMsg);
146-
setIsLoading(false);
147-
setLoading(false);
148-
}
149-
}
150-
setIsLoading(false);
151-
setLoading(false);
152-
return;
153-
}
154-
155-
// Parse JSON only if response is ok
156-
const data = await response.json();
86+
const data = await signIn(formData.username, formData.password);
15787

15888
if (data.accessToken && data.refreshToken) {
159-
// Update Redux state with both tokens
160-
const user = {
161-
id: data.user?.id || formData.username,
89+
const user = data.user ?? {
90+
id: formData.username,
16291
email: formData.username,
163-
username: data.user?.username || formData.username.split('@')[0],
92+
username: formData.username.split('@')[0],
16493
};
165-
94+
16695
loginSuccess(user, data.accessToken, data.refreshToken);
167-
168-
// Show success toast
16996
showSuccess('Login Successful', 'Welcome back!');
170-
setIsLoading(false);
171-
setLoading(false);
172-
173-
// Redirect to dashboard
17497
router.push('/dashboard');
175-
} else {
176-
const errorMsg = 'Invalid response from server. Please try again.';
177-
showError('Invalid Response', errorMsg);
178-
loginFailure(errorMsg);
179-
setIsLoading(false);
180-
setLoading(false);
98+
return;
18199
}
182-
} catch (error) {
183-
console.error('Sign in error:', error);
184-
const errorMsg = 'Network error.';
185-
showError('Network Error', errorMsg);
100+
101+
const errorMsg = 'Invalid response from server. Please try again.';
102+
showError('Invalid Response', errorMsg);
186103
loginFailure(errorMsg);
187-
setIsLoading(false);
188-
setLoading(false);
104+
} catch (error) {
105+
const message =
106+
error instanceof Error ? error.message : 'Login failed. Please try again.';
107+
const isNetwork = message === 'Unexpected error occurred' || /network/i.test(message);
108+
showError(isNetwork ? 'Network Error' : 'Login Failed', message);
109+
loginFailure(message);
189110
} finally {
190111
setIsLoading(false);
191112
setLoading(false);
192113
}
193114
};
194115

195-
const handleGoogleSignIn = () => {
196-
showInfo('Google Sign-In', 'Redirecting to Google authentication...');
197-
window.location.href = "http://localhost:3000/auth/google-authentication";
116+
const handleGoogleSignIn = async () => {
117+
try {
118+
showInfo('Google Sign-In', 'Opening Google authentication...');
119+
await signInWithGoogle();
120+
showSuccess('Login Successful', 'Welcome back!');
121+
router.push('/dashboard');
122+
} catch (error) {
123+
const message =
124+
error instanceof Error ? error.message : 'Google sign-in failed';
125+
showError('Google Sign-In Failed', message);
126+
}
127+
};
128+
129+
const handleGuestPlay = async () => {
130+
try {
131+
await startGuestSession();
132+
showSuccess('Guest session started', 'You have 15 minutes to browse puzzles.');
133+
router.push('/puzzles');
134+
} catch (error) {
135+
const message =
136+
error instanceof Error ? error.message : 'Could not start a guest session';
137+
showError('Guest Play Failed', message);
138+
}
198139
};
199140

200141
const handleWalletSelect = async (walletType: ModalWalletType) => {
@@ -339,6 +280,15 @@ const SignInPage = () => {
339280
{isLoggingIn && 'Verifying...'}
340281
{!isConnecting && !isSigning && !isLoggingIn && 'Connect Wallet'}
341282
</button>
283+
284+
<button
285+
type="button"
286+
onClick={handleGuestPlay}
287+
disabled={isGuestStarting}
288+
className="w-full h-12 border-2 border-slate-600 text-slate-200 rounded-lg hover:bg-slate-800/80 transition-colors disabled:opacity-50"
289+
>
290+
{isGuestStarting ? 'Starting guest session...' : 'Continue as guest'}
291+
</button>
342292
</div>
343293

344294
{/* Terms and Privacy */}

frontend/app/auth/signup/page.tsx

Lines changed: 23 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import Image from "next/image";
1010
import ErrorBoundary from "@/components/error/ErrorBoundary";
1111
import { useToast } from "@/components/ui/ToastProvider";
1212
import { useStellarWalletAuth } from "@/hooks/useStellarWalletAuth";
13+
import { useGoogleAuth } from "@/hooks/useGoogleAuth";
14+
import { register } from "@/lib/api/authApi";
1315
import { WalletType } from "@/lib/stellar/types";
1416

1517

@@ -23,6 +25,7 @@ const SignUpPage = () => {
2325
connectAndLogin,
2426
clearError,
2527
} = useStellarWalletAuth();
28+
const { signInWithGoogle } = useGoogleAuth();
2629
const [formData, setFormData] = useState({
2730
username: "",
2831
fullName: "",
@@ -115,156 +118,36 @@ const SignUpPage = () => {
115118
}
116119

117120
try {
118-
// Format request body to match server expectations
119-
const requestBody = {
121+
await register({
120122
email: formData.email,
121123
username: formData.username,
122-
fullname: formData.fullName, // Server expects lowercase 'n'
124+
fullname: formData.fullName,
123125
password: formData.password,
124-
userRole: "user", // Default role
125-
provider: "local", // Local registration
126-
};
127-
128-
console.log("Sending request with data:", requestBody); // Debug log
129-
130-
const response = await fetch(
131-
"http://localhost:3000/users",
132-
{
133-
method: "POST",
134-
headers: {
135-
"Content-Type": "application/json",
136-
},
137-
body: JSON.stringify(requestBody),
138-
},
139-
);
126+
passwordConfirm: formData.password,
127+
});
140128

141-
// Log response details for debugging
142-
console.log("Response status:", response.status);
143-
console.log("Response headers:", response.headers);
144-
145-
// Checking if response is ok before trying to parse JSON
146-
if (!response.ok) {
147-
let errorMessage = "Registration failed. Please try again.";
148-
149-
try {
150-
const errorData = await response.json();
151-
console.log("Error response data:", errorData); // Debug log
152-
153-
// Safely extract error message
154-
if (typeof errorData === "object" && errorData !== null) {
155-
if (typeof errorData.message === "string") {
156-
errorMessage = errorData.message;
157-
} else if (typeof errorData.error === "string") {
158-
errorMessage = errorData.error;
159-
} else if (
160-
Array.isArray(errorData.errors) &&
161-
errorData.errors.length > 0
162-
) {
163-
errorMessage = errorData.errors[0];
164-
}
165-
}
166-
167-
// Handle specific error cases
168-
if (response.status === 409) {
169-
showError(
170-
"Account Already Exists",
171-
errorMessage ||
172-
"User already exists with this email or username.",
173-
);
174-
} else if (response.status === 400) {
175-
showError(
176-
"Invalid Input",
177-
errorMessage ||
178-
"Invalid input data. Please check your information.",
179-
);
180-
} else if (response.status >= 500) {
181-
showError("Server Error", "Server error. Please try again later.");
182-
} else {
183-
showError("Registration Failed", errorMessage);
184-
}
185-
} catch (parseError) {
186-
console.error("Error parsing response:", parseError);
187-
// If response isn't JSON, use status-based messages
188-
if (response.status === 409) {
189-
showError(
190-
"Account Already Exists",
191-
"An account with this email or username already exists.",
192-
);
193-
} else if (response.status === 400) {
194-
showError(
195-
"Invalid Input",
196-
"Please check your input and try again.",
197-
);
198-
} else {
199-
showError(
200-
"Registration Failed",
201-
`Error ${response.status}: ${response.statusText || "Please try again."}`,
202-
);
203-
}
204-
}
205-
setIsLoading(false);
206-
return;
207-
}
208-
209-
// Parse JSON only if response is ok
210-
const data = await response.json();
211-
console.log("Success response data:", data); // Debug log
212-
213-
if (
214-
data.accessToken ||
215-
data.id ||
216-
data.email ||
217-
data.message === "User created successfully" ||
218-
data.success
219-
) {
220-
// If we get a token, store it
221-
if (data.accessToken) {
222-
try {
223-
localStorage.setItem("accessToken", data.accessToken);
224-
} catch (storageError) {
225-
console.warn("Could not save token to localStorage:", storageError);
226-
}
227-
}
228-
229-
// Show success toast
230-
showSuccess("Registration Successful", "Welcome to Mind Block!");
231-
232-
// Redirect to signin page or dashboard based on whether we got a token
233-
setTimeout(() => {
234-
if (data.accessToken) {
235-
router.push("/dashboard");
236-
} else {
237-
router.push("/auth/signin");
238-
}
239-
}, 1000); // Small delay to show success message
240-
} else {
241-
showError(
242-
"Invalid Response",
243-
"Invalid response from server. Please try again.",
244-
);
245-
}
129+
showSuccess("Account created", "You can now sign in.");
130+
router.push("/auth/signin");
246131
} catch (error) {
247-
console.error("Sign up error:", error);
248-
if (error instanceof TypeError && error.message.includes("fetch")) {
249-
showError(
250-
"Network Error",
251-
"Could not connect to the server. Please check your internet connection and try again.",
252-
);
253-
} else {
254-
showError(
255-
"Network Error",
256-
"An unexpected error occurred. Please try again.",
257-
);
258-
}
132+
const message =
133+
error instanceof Error ? error.message : "Registration failed. Please try again.";
134+
showError("Registration Failed", message);
259135
} finally {
260136
setIsLoading(false);
261137
}
262138
};
263139

264-
const handleGoogleSignUp = () => {
265-
showInfo("Google Sign-Up", "Redirecting to Google authentication...");
266-
window.location.href =
267-
"http://localhost:3000/auth/google-authentication";
140+
const handleGoogleSignUp = async () => {
141+
try {
142+
showInfo("Google Sign-Up", "Opening Google authentication...");
143+
await signInWithGoogle();
144+
showSuccess("Login Successful", "Welcome to Mind Block!");
145+
router.push("/dashboard");
146+
} catch (error) {
147+
const message =
148+
error instanceof Error ? error.message : "Google sign-up failed";
149+
showError("Google Sign-Up Failed", message);
150+
}
268151
};
269152

270153
const handleWalletConnect = async () => {

0 commit comments

Comments
 (0)