2025-03-12 16:13:03 -06:00
|
|
|
import { useState, useEffect } from "react";
|
|
|
|
|
import {
|
|
|
|
|
BrowserRouter as Router,
|
|
|
|
|
Routes,
|
|
|
|
|
Route,
|
|
|
|
|
Navigate,
|
|
|
|
|
} from "react-router-dom";
|
|
|
|
|
import Navbar from "./components/Navbar";
|
|
|
|
|
import Home from "./pages/Home";
|
|
|
|
|
import Settings from "./pages/Settings";
|
|
|
|
|
import Selling from "./pages/Selling";
|
|
|
|
|
import Transactions from "./pages/Transactions";
|
|
|
|
|
import Favorites from "./pages/Favorites";
|
|
|
|
|
import ProductDetail from "./pages/ProductDetail";
|
2025-03-18 18:09:15 -06:00
|
|
|
import ItemForm from "./pages/MyListings";
|
2025-02-19 12:33:38 -07:00
|
|
|
|
|
|
|
|
function App() {
|
2025-03-12 16:13:03 -06:00
|
|
|
// Authentication state - initialize from localStorage if available
|
|
|
|
|
const [isAuthenticated, setIsAuthenticated] = useState(() => {
|
2025-03-14 19:54:20 -06:00
|
|
|
return sessionStorage.getItem("isAuthenticated") === "true";
|
2025-03-12 16:13:03 -06:00
|
|
|
});
|
|
|
|
|
const [user, setUser] = useState(() => {
|
2025-03-14 19:54:20 -06:00
|
|
|
const savedUser = sessionStorage.getItem("user");
|
2025-03-12 16:13:03 -06:00
|
|
|
return savedUser ? JSON.parse(savedUser) : null;
|
|
|
|
|
});
|
|
|
|
|
|
2025-03-05 22:30:52 -07:00
|
|
|
// UI state for login/signup form
|
|
|
|
|
const [isSignUp, setIsSignUp] = useState(false);
|
|
|
|
|
const [showImage, setShowImage] = useState(true);
|
2025-03-12 16:13:03 -06:00
|
|
|
const [error, setError] = useState("");
|
|
|
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
|
|
|
|
2025-03-14 16:14:10 -06:00
|
|
|
// New verification states
|
|
|
|
|
const [verificationStep, setVerificationStep] = useState("initial"); // 'initial', 'code-sent', 'verifying'
|
|
|
|
|
const [tempUserData, setTempUserData] = useState(null);
|
|
|
|
|
|
2025-03-05 22:30:52 -07:00
|
|
|
// Auto-hide image on smaller screens
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const handleResize = () => {
|
|
|
|
|
if (window.innerWidth < 768) {
|
|
|
|
|
setShowImage(false);
|
|
|
|
|
} else {
|
|
|
|
|
setShowImage(true);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
handleResize();
|
2025-03-12 16:13:03 -06:00
|
|
|
|
|
|
|
|
window.addEventListener("resize", handleResize);
|
|
|
|
|
|
|
|
|
|
return () => window.removeEventListener("resize", handleResize);
|
2025-03-05 22:30:52 -07:00
|
|
|
}, []);
|
|
|
|
|
|
2025-03-14 16:14:10 -06:00
|
|
|
// Send verification code
|
|
|
|
|
const sendVerificationCode = async (userData) => {
|
|
|
|
|
try {
|
|
|
|
|
setIsLoading(true);
|
|
|
|
|
setError("");
|
|
|
|
|
|
|
|
|
|
console.log("Sending verification code to:", userData.email);
|
|
|
|
|
|
|
|
|
|
// Make API call to send verification code
|
|
|
|
|
const response = await fetch("http://localhost:3030/send-verification", {
|
|
|
|
|
method: "POST",
|
|
|
|
|
headers: {
|
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
|
},
|
|
|
|
|
body: JSON.stringify({
|
|
|
|
|
email: userData.email,
|
|
|
|
|
// Add any other required fields
|
|
|
|
|
}),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!response.ok) {
|
|
|
|
|
throw new Error("Failed to send verification code");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const result = await response.json();
|
|
|
|
|
|
|
|
|
|
if (result.success) {
|
|
|
|
|
// Store temporary user data
|
|
|
|
|
setTempUserData(userData);
|
|
|
|
|
// Move to verification step
|
|
|
|
|
setVerificationStep("code-sent");
|
|
|
|
|
console.log("Verification code sent successfully");
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
setError(result.message || "Failed to send verification code");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.error("Error sending verification code:", err);
|
|
|
|
|
setError("Failed to send verification code. Please try again.");
|
|
|
|
|
return false;
|
|
|
|
|
} finally {
|
|
|
|
|
setIsLoading(false);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Verify code
|
|
|
|
|
const verifyCode = async (code) => {
|
|
|
|
|
try {
|
|
|
|
|
setIsLoading(true);
|
|
|
|
|
setError("");
|
|
|
|
|
|
|
|
|
|
console.log("Verifying code:", code);
|
|
|
|
|
|
|
|
|
|
// Make API call to verify code
|
|
|
|
|
const response = await fetch("http://localhost:3030/verify-code", {
|
|
|
|
|
method: "POST",
|
|
|
|
|
headers: {
|
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
|
},
|
|
|
|
|
body: JSON.stringify({
|
|
|
|
|
email: tempUserData.email,
|
|
|
|
|
code: code,
|
|
|
|
|
}),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!response.ok) {
|
|
|
|
|
throw new Error("Failed to verify code");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const result = await response.json();
|
|
|
|
|
|
|
|
|
|
if (result.success) {
|
|
|
|
|
console.log("Code verified successfully");
|
|
|
|
|
// Proceed to complete signup
|
|
|
|
|
return await completeSignUp(tempUserData);
|
|
|
|
|
} else {
|
|
|
|
|
setError(result.message || "Invalid verification code");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.error("Error verifying code:", err);
|
|
|
|
|
setError("Failed to verify code. Please try again.");
|
|
|
|
|
return false;
|
|
|
|
|
} finally {
|
|
|
|
|
setIsLoading(false);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Complete signup
|
|
|
|
|
const completeSignUp = async (userData) => {
|
|
|
|
|
try {
|
|
|
|
|
setIsLoading(true);
|
|
|
|
|
setError("");
|
|
|
|
|
|
|
|
|
|
console.log("Completing signup for:", userData.email);
|
|
|
|
|
console.log(userData);
|
|
|
|
|
|
|
|
|
|
// Make API call to complete signup
|
|
|
|
|
const response = await fetch("http://localhost:3030/complete-signup", {
|
|
|
|
|
method: "POST",
|
|
|
|
|
headers: {
|
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
|
},
|
|
|
|
|
body: JSON.stringify(userData),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!response.ok) {
|
|
|
|
|
throw new Error("Failed to complete signup");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const result = await response.json();
|
2025-03-18 18:09:15 -06:00
|
|
|
console.log(result);
|
2025-03-14 16:14:10 -06:00
|
|
|
|
|
|
|
|
if (result.success) {
|
|
|
|
|
// Create user object from API response
|
|
|
|
|
const newUser = {
|
|
|
|
|
name: result.name || userData.name,
|
|
|
|
|
email: result.email || userData.email,
|
|
|
|
|
UCID: result.UCID || userData.ucid,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Set authenticated user
|
|
|
|
|
setUser(newUser);
|
|
|
|
|
setIsAuthenticated(true);
|
|
|
|
|
|
|
|
|
|
// Save to localStorage to persist across refreshes
|
2025-03-14 19:54:20 -06:00
|
|
|
sessionStorage.setItem("isAuthenticated", "true");
|
|
|
|
|
sessionStorage.setItem("user", JSON.stringify(newUser));
|
2025-03-14 16:14:10 -06:00
|
|
|
|
|
|
|
|
// Reset verification steps
|
|
|
|
|
setVerificationStep("initial");
|
|
|
|
|
setTempUserData(null);
|
|
|
|
|
|
|
|
|
|
console.log("Signup completed successfully");
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
setError(result.message || "Failed to complete signup");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.error("Error completing signup:", err);
|
|
|
|
|
setError("Failed to complete signup. Please try again.");
|
|
|
|
|
return false;
|
|
|
|
|
} finally {
|
|
|
|
|
setIsLoading(false);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2025-03-12 16:13:03 -06:00
|
|
|
const handleSubmit = async (e) => {
|
2025-03-05 22:30:52 -07:00
|
|
|
e.preventDefault();
|
2025-03-12 16:13:03 -06:00
|
|
|
setIsLoading(true);
|
|
|
|
|
setError("");
|
|
|
|
|
|
|
|
|
|
// Get form data directly from the form
|
|
|
|
|
const formData = new FormData(e.target);
|
|
|
|
|
const formValues = Object.fromEntries(formData.entries());
|
|
|
|
|
|
2025-03-05 22:30:52 -07:00
|
|
|
// Validate email and password
|
2025-03-12 16:13:03 -06:00
|
|
|
if (!formValues.email || !formValues.password) {
|
|
|
|
|
setError("Email and password are required");
|
|
|
|
|
setIsLoading(false);
|
2025-03-05 22:30:52 -07:00
|
|
|
return;
|
2025-03-14 16:14:10 -06:00
|
|
|
} else if (!formValues.email.endsWith("@ucalgary.ca")) {
|
|
|
|
|
setError("Please use your UCalgary email address (@ucalgary.ca)");
|
|
|
|
|
setIsLoading(false);
|
|
|
|
|
return;
|
2025-03-05 22:30:52 -07:00
|
|
|
}
|
2025-03-12 16:13:03 -06:00
|
|
|
try {
|
|
|
|
|
if (isSignUp) {
|
2025-03-14 16:14:10 -06:00
|
|
|
// Handle Sign Up with verification
|
2025-03-12 16:13:03 -06:00
|
|
|
console.log("Sign Up Form Data:", formValues);
|
|
|
|
|
|
2025-03-14 16:14:10 -06:00
|
|
|
// Create a user object with the form data
|
2025-03-12 16:13:03 -06:00
|
|
|
const newUser = {
|
|
|
|
|
name: formValues.name,
|
|
|
|
|
email: formValues.email,
|
2025-03-14 16:14:10 -06:00
|
|
|
UCID: formValues.ucid,
|
2025-03-12 16:13:03 -06:00
|
|
|
phone: formValues.phone,
|
2025-03-14 16:14:10 -06:00
|
|
|
password: formValues.password, // This will be needed for the final signup
|
|
|
|
|
address: "NOT_GIVEN",
|
|
|
|
|
client: 1,
|
|
|
|
|
admin: 0,
|
2025-03-12 16:13:03 -06:00
|
|
|
};
|
|
|
|
|
|
2025-03-14 16:14:10 -06:00
|
|
|
// Send verification code
|
|
|
|
|
await sendVerificationCode(newUser);
|
2025-03-05 22:30:52 -07:00
|
|
|
} else {
|
2025-03-12 16:13:03 -06:00
|
|
|
// Handle Login with API
|
|
|
|
|
console.log("Login Attempt:", {
|
|
|
|
|
email: formValues.email,
|
|
|
|
|
password: formValues.password,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Make API call to localhost:3030/find_user
|
|
|
|
|
const response = await fetch("http://localhost:3030/find_user", {
|
|
|
|
|
method: "POST",
|
|
|
|
|
headers: {
|
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
|
},
|
|
|
|
|
body: JSON.stringify({
|
|
|
|
|
email: formValues.email,
|
|
|
|
|
password: formValues.password,
|
|
|
|
|
}),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!response.ok) {
|
|
|
|
|
throw new Error("Network response was not ok");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const userData = await response.json();
|
|
|
|
|
|
|
|
|
|
if (userData && userData.found) {
|
|
|
|
|
// Create user object
|
|
|
|
|
const userObj = {
|
2025-03-14 19:54:20 -06:00
|
|
|
ID: userData.userID,
|
|
|
|
|
name: userData.name,
|
2025-03-12 16:13:03 -06:00
|
|
|
email: userData.email,
|
|
|
|
|
// Add any other user data returned from the API
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Set authenticated user with data from API
|
|
|
|
|
setUser(userObj);
|
|
|
|
|
setIsAuthenticated(true);
|
|
|
|
|
|
|
|
|
|
// Save to localStorage to persist across refreshes
|
2025-03-14 19:54:20 -06:00
|
|
|
sessionStorage.setItem("isAuthenticated", "true");
|
|
|
|
|
sessionStorage.setItem("user", JSON.stringify(userObj));
|
|
|
|
|
|
|
|
|
|
sessionStorage.getItem("user");
|
2025-03-12 16:13:03 -06:00
|
|
|
|
|
|
|
|
console.log("Login successful for:", userData.email);
|
|
|
|
|
} else {
|
|
|
|
|
// Show error message for invalid credentials
|
|
|
|
|
setError("Invalid email or password");
|
|
|
|
|
console.log("Login failed: Invalid credentials");
|
|
|
|
|
}
|
2025-03-05 22:30:52 -07:00
|
|
|
}
|
2025-03-12 16:13:03 -06:00
|
|
|
} catch (err) {
|
|
|
|
|
console.error("Authentication error:", err);
|
|
|
|
|
setError("Authentication failed. Please try again later.");
|
|
|
|
|
} finally {
|
|
|
|
|
setIsLoading(false);
|
2025-03-05 22:30:52 -07:00
|
|
|
}
|
|
|
|
|
};
|
2025-03-12 16:13:03 -06:00
|
|
|
|
2025-03-14 16:14:10 -06:00
|
|
|
// Handle code verification submission
|
|
|
|
|
const handleVerifySubmit = async (e) => {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
const code = e.target.verificationCode.value;
|
|
|
|
|
|
|
|
|
|
if (!code) {
|
|
|
|
|
setError("Verification code is required");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await verifyCode(code);
|
|
|
|
|
};
|
|
|
|
|
|
2025-03-05 22:30:52 -07:00
|
|
|
const handleLogout = () => {
|
2025-03-12 16:13:03 -06:00
|
|
|
// Clear authentication state
|
2025-03-05 22:30:52 -07:00
|
|
|
setIsAuthenticated(false);
|
|
|
|
|
setUser(null);
|
2025-03-14 16:14:10 -06:00
|
|
|
setVerificationStep("initial");
|
|
|
|
|
setTempUserData(null);
|
2025-03-12 16:13:03 -06:00
|
|
|
|
|
|
|
|
// Clear localStorage
|
2025-03-14 19:54:20 -06:00
|
|
|
//
|
|
|
|
|
sessionStorage.removeItem("user");
|
|
|
|
|
sessionStorage.removeItem("isAuthenticated");
|
2025-03-12 16:13:03 -06:00
|
|
|
|
|
|
|
|
console.log("User logged out");
|
2025-03-05 22:30:52 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const toggleAuthMode = () => {
|
|
|
|
|
setIsSignUp(!isSignUp);
|
2025-03-12 16:13:03 -06:00
|
|
|
setError(""); // Clear errors when switching modes
|
2025-03-14 16:14:10 -06:00
|
|
|
setVerificationStep("initial"); // Reset verification step
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Resend verification code
|
|
|
|
|
const handleResendCode = async () => {
|
|
|
|
|
if (tempUserData) {
|
|
|
|
|
await sendVerificationCode(tempUserData);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Back to signup form
|
|
|
|
|
const handleBackToSignup = () => {
|
|
|
|
|
setVerificationStep("initial");
|
|
|
|
|
setError("");
|
2025-03-05 22:30:52 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Login component
|
|
|
|
|
const LoginComponent = () => (
|
|
|
|
|
<div className="flex h-screen bg-white">
|
|
|
|
|
{/* Image Section - Automatically hidden on mobile */}
|
|
|
|
|
{showImage && (
|
|
|
|
|
<div className="w-1/2 relative">
|
2025-03-12 16:13:03 -06:00
|
|
|
<img
|
2025-03-14 16:14:10 -06:00
|
|
|
src="../market.jpg"
|
2025-03-12 16:13:03 -06:00
|
|
|
alt="auth illustration"
|
2025-03-14 16:14:10 -06:00
|
|
|
className="w-full h-full object-scale-down"
|
2025-03-05 22:30:52 -07:00
|
|
|
/>
|
|
|
|
|
<div className="absolute inset-0"></div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* Auth Form Section */}
|
2025-03-12 16:13:03 -06:00
|
|
|
<div
|
|
|
|
|
className={`${
|
|
|
|
|
showImage ? "w-1/2" : "w-full"
|
|
|
|
|
} bg-white p-8 flex items-center justify-center`}
|
|
|
|
|
>
|
2025-03-05 22:30:52 -07:00
|
|
|
<div className="w-full max-w-md">
|
|
|
|
|
<div className="mb-8 text-center">
|
|
|
|
|
<h2 className="text-2xl font-bold text-gray-800">
|
2025-03-14 16:14:10 -06:00
|
|
|
{isSignUp
|
|
|
|
|
? verificationStep === "initial"
|
|
|
|
|
? "Create Account"
|
|
|
|
|
: "Verify Your Email"
|
|
|
|
|
: "Welcome Back"}
|
2025-03-05 22:30:52 -07:00
|
|
|
</h2>
|
|
|
|
|
<p className="mt-2 text-gray-600">
|
2025-03-14 16:14:10 -06:00
|
|
|
{isSignUp
|
|
|
|
|
? verificationStep === "initial"
|
|
|
|
|
? "Set up your new account"
|
|
|
|
|
: "Enter the verification code sent to your email"
|
|
|
|
|
: "Sign in to your account"}
|
2025-03-05 22:30:52 -07:00
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="border border-gray-200 shadow-sm p-6">
|
|
|
|
|
{error && (
|
|
|
|
|
<div className="mb-4 p-2 bg-red-50 text-red-600 text-sm border border-red-200 rounded">
|
|
|
|
|
{error}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
2025-03-12 16:13:03 -06:00
|
|
|
|
2025-03-14 16:14:10 -06:00
|
|
|
{/* Signup or Login Form */}
|
|
|
|
|
{(!isSignUp || (isSignUp && verificationStep === "initial")) && (
|
|
|
|
|
<form onSubmit={handleSubmit} className="space-y-4">
|
|
|
|
|
{/* Name field - only for signup */}
|
|
|
|
|
{isSignUp && (
|
|
|
|
|
<div>
|
|
|
|
|
<label
|
|
|
|
|
htmlFor="name"
|
|
|
|
|
className="block mb-1 text-sm font-medium text-gray-800"
|
|
|
|
|
>
|
|
|
|
|
Full Name
|
|
|
|
|
</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
id="name"
|
|
|
|
|
name="name"
|
|
|
|
|
placeholder="Enter your name"
|
|
|
|
|
className="w-full px-4 py-2 border border-gray-300 bg-white text-gray-800 focus:outline-none focus:border-green-500 focus:ring-1 focus:ring-green-500"
|
|
|
|
|
required={isSignUp}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{isSignUp && (
|
|
|
|
|
<div>
|
|
|
|
|
<label
|
|
|
|
|
htmlFor="ucid"
|
|
|
|
|
className="block mb-1 text-sm font-medium text-gray-800"
|
|
|
|
|
>
|
|
|
|
|
UCID
|
|
|
|
|
</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
id="ucid"
|
|
|
|
|
name="ucid"
|
|
|
|
|
placeholder="1234567"
|
|
|
|
|
className="w-full px-4 py-2 border border-gray-300 bg-white text-gray-800 focus:outline-none focus:border-green-500 focus:ring-1 focus:ring-green-500"
|
|
|
|
|
required={isSignUp}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
2025-03-05 22:30:52 -07:00
|
|
|
<div>
|
2025-03-12 16:13:03 -06:00
|
|
|
<label
|
2025-03-14 16:14:10 -06:00
|
|
|
htmlFor="email"
|
2025-03-12 16:13:03 -06:00
|
|
|
className="block mb-1 text-sm font-medium text-gray-800"
|
|
|
|
|
>
|
2025-03-14 16:14:10 -06:00
|
|
|
Email
|
2025-03-05 22:30:52 -07:00
|
|
|
</label>
|
|
|
|
|
<input
|
2025-03-14 16:14:10 -06:00
|
|
|
type="email"
|
|
|
|
|
id="email"
|
|
|
|
|
name="email"
|
|
|
|
|
placeholder="your.email@ucalgary.ca"
|
2025-03-05 22:30:52 -07:00
|
|
|
className="w-full px-4 py-2 border border-gray-300 bg-white text-gray-800 focus:outline-none focus:border-green-500 focus:ring-1 focus:ring-green-500"
|
2025-03-14 16:14:10 -06:00
|
|
|
required
|
2025-03-05 22:30:52 -07:00
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
2025-03-14 16:14:10 -06:00
|
|
|
{isSignUp && (
|
|
|
|
|
<div>
|
|
|
|
|
<label
|
|
|
|
|
htmlFor="phone"
|
|
|
|
|
className="block mb-1 text-sm font-medium text-gray-800"
|
|
|
|
|
>
|
|
|
|
|
Phone Number
|
|
|
|
|
</label>
|
|
|
|
|
<input
|
|
|
|
|
type="tel"
|
|
|
|
|
id="phone"
|
|
|
|
|
name="phone"
|
|
|
|
|
placeholder="+1(123)456 7890"
|
|
|
|
|
className="w-full px-4 py-2 border border-gray-300 bg-white text-gray-800 focus:outline-none focus:border-green-500 focus:ring-1 focus:ring-green-500"
|
|
|
|
|
required={isSignUp}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
2025-03-05 22:30:52 -07:00
|
|
|
<div>
|
2025-03-12 16:13:03 -06:00
|
|
|
<label
|
2025-03-14 16:14:10 -06:00
|
|
|
htmlFor="password"
|
2025-03-12 16:13:03 -06:00
|
|
|
className="block mb-1 text-sm font-medium text-gray-800"
|
|
|
|
|
>
|
2025-03-14 16:14:10 -06:00
|
|
|
Password
|
2025-03-05 22:30:52 -07:00
|
|
|
</label>
|
|
|
|
|
<input
|
2025-03-14 16:14:10 -06:00
|
|
|
type="password"
|
|
|
|
|
id="password"
|
|
|
|
|
name="password"
|
|
|
|
|
placeholder={
|
|
|
|
|
isSignUp
|
|
|
|
|
? "Create a secure password"
|
|
|
|
|
: "Enter your password"
|
|
|
|
|
}
|
2025-03-05 22:30:52 -07:00
|
|
|
className="w-full px-4 py-2 border border-gray-300 bg-white text-gray-800 focus:outline-none focus:border-green-500 focus:ring-1 focus:ring-green-500"
|
2025-03-14 16:14:10 -06:00
|
|
|
required
|
2025-03-05 22:30:52 -07:00
|
|
|
/>
|
|
|
|
|
</div>
|
2025-03-12 16:13:03 -06:00
|
|
|
|
2025-03-14 16:14:10 -06:00
|
|
|
<div className="pt-4">
|
|
|
|
|
<button
|
|
|
|
|
type="submit"
|
|
|
|
|
disabled={isLoading}
|
|
|
|
|
className="w-full px-6 py-2 text-base font-medium text-white bg-green-500 hover:bg-green-600 focus:outline-none focus:ring-2 focus:ring-green-400 focus:ring-offset-2 transition-colors disabled:bg-green-300"
|
|
|
|
|
>
|
|
|
|
|
{isLoading
|
|
|
|
|
? "Please wait..."
|
|
|
|
|
: isSignUp
|
|
|
|
|
? "Create Account"
|
|
|
|
|
: "Sign In"}
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</form>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* Verification Code Form */}
|
|
|
|
|
{isSignUp && verificationStep === "code-sent" && (
|
|
|
|
|
<form onSubmit={handleVerifySubmit} className="space-y-4">
|
2025-03-05 22:30:52 -07:00
|
|
|
<div>
|
2025-03-12 16:13:03 -06:00
|
|
|
<label
|
2025-03-14 16:14:10 -06:00
|
|
|
htmlFor="verificationCode"
|
2025-03-12 16:13:03 -06:00
|
|
|
className="block mb-1 text-sm font-medium text-gray-800"
|
|
|
|
|
>
|
2025-03-14 16:14:10 -06:00
|
|
|
Verification Code
|
2025-03-05 22:30:52 -07:00
|
|
|
</label>
|
|
|
|
|
<input
|
2025-03-14 16:14:10 -06:00
|
|
|
type="text"
|
|
|
|
|
id="verificationCode"
|
|
|
|
|
name="verificationCode"
|
|
|
|
|
placeholder="Enter the 6-digit code"
|
2025-03-05 22:30:52 -07:00
|
|
|
className="w-full px-4 py-2 border border-gray-300 bg-white text-gray-800 focus:outline-none focus:border-green-500 focus:ring-1 focus:ring-green-500"
|
2025-03-14 16:14:10 -06:00
|
|
|
required
|
2025-03-05 22:30:52 -07:00
|
|
|
/>
|
2025-03-14 16:14:10 -06:00
|
|
|
<p className="mt-1 text-xs text-gray-500">
|
|
|
|
|
We've sent a verification code to {tempUserData?.email}
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="pt-4">
|
|
|
|
|
<button
|
|
|
|
|
type="submit"
|
|
|
|
|
disabled={isLoading}
|
|
|
|
|
className="w-full px-6 py-2 text-base font-medium text-white bg-green-500 hover:bg-green-600 focus:outline-none focus:ring-2 focus:ring-green-400 focus:ring-offset-2 transition-colors disabled:bg-green-300"
|
|
|
|
|
>
|
|
|
|
|
{isLoading ? "Please wait..." : "Verify Code"}
|
|
|
|
|
</button>
|
2025-03-05 22:30:52 -07:00
|
|
|
</div>
|
|
|
|
|
|
2025-03-14 16:14:10 -06:00
|
|
|
<div className="flex justify-between mt-4">
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
onClick={handleBackToSignup}
|
|
|
|
|
className="text-sm text-gray-600 hover:text-gray-800"
|
|
|
|
|
>
|
|
|
|
|
Back to signup
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
onClick={handleResendCode}
|
|
|
|
|
disabled={isLoading}
|
|
|
|
|
className="text-sm text-green-500 hover:text-green-700"
|
|
|
|
|
>
|
|
|
|
|
Resend code
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</form>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* Auth mode toggle */}
|
|
|
|
|
{verificationStep === "initial" && (
|
|
|
|
|
<div className="mt-6 text-center text-sm text-gray-500">
|
|
|
|
|
<p>
|
|
|
|
|
{isSignUp
|
|
|
|
|
? "Already have an account?"
|
|
|
|
|
: "Don't have an account?"}{" "}
|
|
|
|
|
<button
|
|
|
|
|
onClick={toggleAuthMode}
|
|
|
|
|
type="button"
|
|
|
|
|
className="text-green-500 font-medium hover:text-green-700"
|
|
|
|
|
>
|
|
|
|
|
{isSignUp ? "Sign in" : "Sign up"}
|
|
|
|
|
</button>
|
|
|
|
|
</p>
|
2025-03-05 22:30:52 -07:00
|
|
|
</div>
|
2025-03-14 16:14:10 -06:00
|
|
|
)}
|
2025-03-05 22:30:52 -07:00
|
|
|
</div>
|
|
|
|
|
</div>
|
2025-02-19 12:33:38 -07:00
|
|
|
</div>
|
2025-03-05 22:30:52 -07:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Protected route component
|
|
|
|
|
const ProtectedRoute = ({ children }) => {
|
|
|
|
|
if (!isAuthenticated) {
|
|
|
|
|
return <Navigate to="/login" />;
|
|
|
|
|
}
|
2025-03-12 16:13:03 -06:00
|
|
|
|
2025-03-05 22:30:52 -07:00
|
|
|
return children;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Router>
|
|
|
|
|
<div className="min-h-screen bg-gray-50">
|
|
|
|
|
{/* Only show navbar when authenticated */}
|
2025-03-12 16:13:03 -06:00
|
|
|
{isAuthenticated && (
|
|
|
|
|
<Navbar onLogout={handleLogout} userName={user?.name} />
|
|
|
|
|
)}
|
2025-03-05 22:30:52 -07:00
|
|
|
<Routes>
|
|
|
|
|
{/* Public routes */}
|
2025-03-12 16:13:03 -06:00
|
|
|
<Route
|
|
|
|
|
path="/login"
|
|
|
|
|
element={isAuthenticated ? <Navigate to="/" /> : <LoginComponent />}
|
2025-03-05 22:30:52 -07:00
|
|
|
/>
|
|
|
|
|
{/* Protected routes */}
|
2025-03-12 16:13:03 -06:00
|
|
|
<Route
|
|
|
|
|
path="/"
|
2025-03-05 22:30:52 -07:00
|
|
|
element={
|
|
|
|
|
<ProtectedRoute>
|
|
|
|
|
<div className="container mx-auto px-4 py-6">
|
|
|
|
|
<Home />
|
|
|
|
|
</div>
|
|
|
|
|
</ProtectedRoute>
|
2025-03-12 16:13:03 -06:00
|
|
|
}
|
2025-03-05 22:30:52 -07:00
|
|
|
/>
|
2025-03-12 16:13:03 -06:00
|
|
|
<Route
|
|
|
|
|
path="/product/:id"
|
2025-03-05 22:30:52 -07:00
|
|
|
element={
|
|
|
|
|
<ProtectedRoute>
|
|
|
|
|
<ProductDetail />
|
|
|
|
|
</ProtectedRoute>
|
2025-03-12 16:13:03 -06:00
|
|
|
}
|
2025-03-05 22:30:52 -07:00
|
|
|
/>
|
2025-03-12 16:13:03 -06:00
|
|
|
<Route
|
|
|
|
|
path="/settings"
|
2025-03-05 22:30:52 -07:00
|
|
|
element={
|
|
|
|
|
<ProtectedRoute>
|
|
|
|
|
<div className="container mx-auto px-4 py-6">
|
|
|
|
|
<Settings />
|
|
|
|
|
</div>
|
|
|
|
|
</ProtectedRoute>
|
2025-03-12 16:13:03 -06:00
|
|
|
}
|
2025-03-05 22:30:52 -07:00
|
|
|
/>
|
2025-03-12 16:13:03 -06:00
|
|
|
<Route
|
|
|
|
|
path="/selling"
|
2025-03-05 22:30:52 -07:00
|
|
|
element={
|
|
|
|
|
<ProtectedRoute>
|
|
|
|
|
<div className="container mx-auto px-4 py-6">
|
|
|
|
|
<Selling />
|
|
|
|
|
</div>
|
|
|
|
|
</ProtectedRoute>
|
2025-03-12 16:13:03 -06:00
|
|
|
}
|
2025-03-05 22:30:52 -07:00
|
|
|
/>
|
2025-03-18 18:09:15 -06:00
|
|
|
{/* Add new selling routes */}
|
|
|
|
|
<Route
|
|
|
|
|
path="/selling/create"
|
|
|
|
|
element={
|
|
|
|
|
<ProtectedRoute>
|
|
|
|
|
<div className="container mx-auto px-4 py-6">
|
|
|
|
|
<ItemForm />
|
|
|
|
|
</div>
|
|
|
|
|
</ProtectedRoute>
|
|
|
|
|
}
|
|
|
|
|
/>
|
|
|
|
|
<Route
|
|
|
|
|
path="/selling/edit/:id"
|
|
|
|
|
element={
|
|
|
|
|
<ProtectedRoute>
|
|
|
|
|
<div className="container mx-auto px-4 py-6">
|
|
|
|
|
<ItemForm />
|
|
|
|
|
</div>
|
|
|
|
|
</ProtectedRoute>
|
|
|
|
|
}
|
|
|
|
|
/>
|
2025-03-12 16:13:03 -06:00
|
|
|
<Route
|
|
|
|
|
path="/transactions"
|
2025-03-05 22:30:52 -07:00
|
|
|
element={
|
|
|
|
|
<ProtectedRoute>
|
|
|
|
|
<div className="container mx-auto px-4 py-6">
|
|
|
|
|
<Transactions />
|
|
|
|
|
</div>
|
|
|
|
|
</ProtectedRoute>
|
2025-03-12 16:13:03 -06:00
|
|
|
}
|
2025-03-05 22:30:52 -07:00
|
|
|
/>
|
2025-03-12 16:13:03 -06:00
|
|
|
<Route
|
|
|
|
|
path="/favorites"
|
2025-03-05 22:30:52 -07:00
|
|
|
element={
|
|
|
|
|
<ProtectedRoute>
|
|
|
|
|
<div className="container mx-auto px-4 py-6">
|
|
|
|
|
<Favorites />
|
|
|
|
|
</div>
|
|
|
|
|
</ProtectedRoute>
|
2025-03-12 16:13:03 -06:00
|
|
|
}
|
2025-03-05 22:30:52 -07:00
|
|
|
/>
|
|
|
|
|
{/* Redirect to login for any unmatched routes */}
|
2025-03-12 16:13:03 -06:00
|
|
|
<Route
|
|
|
|
|
path="*"
|
|
|
|
|
element={<Navigate to={isAuthenticated ? "/" : "/login"} />}
|
|
|
|
|
/>
|
2025-03-05 22:30:52 -07:00
|
|
|
</Routes>
|
2025-02-19 12:33:38 -07:00
|
|
|
</div>
|
2025-03-05 22:30:52 -07:00
|
|
|
</Router>
|
|
|
|
|
);
|
2025-02-19 12:33:38 -07:00
|
|
|
}
|
|
|
|
|
|
2025-03-12 16:13:03 -06:00
|
|
|
export default App;
|