diff --git a/frontend/src/components/layout/BackstoryRoutes.tsx b/frontend/src/components/layout/BackstoryRoutes.tsx
index a279367..c7eb709 100644
--- a/frontend/src/components/layout/BackstoryRoutes.tsx
+++ b/frontend/src/components/layout/BackstoryRoutes.tsx
@@ -19,6 +19,7 @@ import { GenerateCandidate } from "pages/GenerateCandidate";
import { ControlsPage } from 'pages/ControlsPage';
import { LoginPage } from "pages/LoginPage";
import { CandidateDashboardPage } from "pages/CandidateDashboardPage"
+import { EmailVerificationPage } from "components/EmailVerificationComponents";
const ProfilePage = () => (Profile);
const BackstoryPage = () => (Backstory);
@@ -58,9 +59,11 @@ const getBackstoryDynamicRoutes = (props: BackstoryDynamicRoutesProps): ReactNod
if (!user) {
routes.push()} />);
routes.push(} />);
+ routes.push(} />);
routes.push(} />);
} else {
routes.push(} />);
+ routes.push(} />);
routes.push(} />);
if (user.userType === 'candidate') {
diff --git a/frontend/src/pages/BetaPage.tsx b/frontend/src/pages/BetaPage.tsx
index d2f2b41..7555fd9 100644
--- a/frontend/src/pages/BetaPage.tsx
+++ b/frontend/src/pages/BetaPage.tsx
@@ -38,7 +38,7 @@ const BetaPage: React.FC = ({
const location = useLocation();
if (!children) {
- children = (The page you requested ({location.pathname.replace(/^\//, '')}) is not yet read.);
+ children = (The page you requested ({location.pathname.replace(/^\//, '')}) is not yet ready.);
}
console.log("BetaPage", children);
diff --git a/frontend/src/pages/LoginPage.tsx b/frontend/src/pages/LoginPage.tsx
index 041b633..735425e 100644
--- a/frontend/src/pages/LoginPage.tsx
+++ b/frontend/src/pages/LoginPage.tsx
@@ -3,174 +3,43 @@ import {
Box,
Container,
Paper,
- TextField,
- Button,
Typography,
Grid,
Alert,
- CircularProgress,
Tabs,
Tab,
Card,
CardContent,
Divider,
Avatar,
- IconButton,
- InputAdornment,
- List,
- ListItem,
- ListItemIcon,
- ListItemText,
- Collapse,
- FormControl,
- FormLabel,
- RadioGroup,
- FormControlLabel,
- Radio,
- Chip
} from '@mui/material';
import {
Person,
PersonAdd,
AccountCircle,
- Visibility,
- VisibilityOff,
- CheckCircle,
- Cancel,
- ExpandLess,
- ExpandMore,
- Business
} from '@mui/icons-material';
import 'react-phone-number-input/style.css';
-import PhoneInput from 'react-phone-number-input';
-import { E164Number } from 'libphonenumber-js/core';
import './LoginPage.css';
-import { ApiClient } from 'services/api-client';
import { useAuth } from 'hooks/AuthContext';
-import { LocationInput } from 'components/LocationInput';
-import { Location } from 'types/types';
import { BackstoryLogo } from 'components/ui/BackstoryLogo';
import { BackstoryPageProps } from 'components/BackstoryTab';
-import { Navigate, useNavigate } from 'react-router-dom';
import { LoginForm } from "components/EmailVerificationComponents";
-import { CandidateRegistrationForm, EmployerRegistrationForm } from "components/RegistrationForms";
-
-type UserRegistrationType = 'candidate' | 'employer';
-
-interface LoginRequest {
- login: string;
- password: string;
-}
-
-interface RegisterRequest {
- userType: UserRegistrationType;
- username: string;
- email: string;
- firstName: string;
- lastName: string;
- password: string;
- confirmPassword: string;
- phone?: string;
- // Employer specific fields (placeholder)
- companyName?: string;
- industry?: string;
- companySize?: string;
-}
-
-interface PasswordRequirement {
- label: string;
- met: boolean;
-}
-
-const apiClient = new ApiClient();
+import { CandidateRegistrationForm } from "components/RegistrationForms";
const LoginPage: React.FC = (props: BackstoryPageProps) => {
- const navigate = useNavigate();
const { setSnack } = props;
const [tabValue, setTabValue] = useState(0);
const [loading, setLoading] = useState(false);
const [success, setSuccess] = useState(null);
- const [phone, setPhone] = useState(null);
- const { createCandidateAccount, guest, user, login, isLoading, error } = useAuth();
- const [passwordValidation, setPasswordValidation] = useState<{ isValid: boolean; issues: string[] }>({ isValid: true, issues: [] });
+ const { guest, user, login, isLoading, error } = useAuth();
const name = (user?.userType === 'candidate') ? user.username : user?.email || '';
- const [location, setLocation] = useState>({});
const [errorMessage, setErrorMessage] = useState(null);
const showGuest: boolean = false;
- // Password visibility states
- const [showLoginPassword, setShowLoginPassword] = useState(false);
- const [showRegisterPassword, setShowRegisterPassword] = useState(false);
- const [showConfirmPassword, setShowConfirmPassword] = useState(false);
- const [showPasswordRequirements, setShowPasswordRequirements] = useState(false);
-
- const handleLocationChange = (location: Partial) => {
- setLocation(location);
- console.log('Location updated:', location);
- };
-
- // Login form state
- const [loginForm, setLoginForm] = useState({
- login: '',
- password: ''
- });
-
- // Register form state
- const [registerForm, setRegisterForm] = useState({
- userType: 'candidate',
- username: '',
- email: '',
- firstName: '',
- lastName: '',
- password: '',
- confirmPassword: '',
- phone: '',
- companyName: '',
- industry: '',
- companySize: ''
- });
-
- // Password requirements validation
- const getPasswordRequirements = (password: string): PasswordRequirement[] => {
- return [
- {
- label: 'At least 8 characters long',
- met: password.length >= 8
- },
- {
- label: 'Contains uppercase letter',
- met: /[A-Z]/.test(password)
- },
- {
- label: 'Contains lowercase letter',
- met: /[a-z]/.test(password)
- },
- {
- label: 'Contains number',
- met: /\d/.test(password)
- },
- {
- label: 'Contains special character (!@#$%^&*)',
- met: /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(password)
- }
- ];
- };
-
- const passwordRequirements = getPasswordRequirements(registerForm.password);
- const passwordsMatch = registerForm.password === registerForm.confirmPassword;
- const hasPasswordMatchError = registerForm.confirmPassword.length > 0 && !passwordsMatch;
-
- useEffect(() => {
- if (phone !== registerForm.phone && phone) {
- console.log({ phone });
- setRegisterForm({ ...registerForm, phone });
- }
- }, [phone, registerForm]);
-
useEffect(() => {
if (!loading || !error) {
return;
@@ -188,110 +57,11 @@ const LoginPage: React.FC = (props: BackstoryPageProps) => {
}
}, [error, loading]);
- const handleLogin = async (e: React.FormEvent) => {
- e.preventDefault();
- setLoading(true);
- setSuccess(null);
-
- const success = await login(loginForm);
- if (success) {
- setSuccess('Login successful!');
- setLoading(false);
- navigate('/chat');
- }
- };
-
- const handlePasswordChange = (password: string) => {
- setRegisterForm(prev => ({ ...prev, password }));
- setPasswordValidation(apiClient.validatePasswordStrength(password));
-
- // Show requirements if password has content and isn't valid
- if (password.length > 0) {
- const requirements = getPasswordRequirements(password);
- const allMet = requirements.every(req => req.met);
- if (!allMet && !showPasswordRequirements) {
- setShowPasswordRequirements(true);
- }
- if (allMet && showPasswordRequirements) {
- setShowPasswordRequirements(false);
- }
- }
- };
-
- const handleUserTypeChange = (event: React.ChangeEvent) => {
- const userType = event.target.value as UserRegistrationType;
- setRegisterForm(prev => ({ ...prev, userType }));
- };
-
- const handleRegister = async (e: React.FormEvent) => {
- e.preventDefault();
-
- // Check if employer registration is attempted
- if (registerForm.userType === 'employer') {
- setSnack('Employer registration is not yet supported. Please contact support for employer account setup.', "warning");
- return;
- }
-
- // Validate passwords match
- if (!passwordsMatch) {
- return;
- }
-
- // Validate password requirements
- const allRequirementsMet = passwordRequirements.every(req => req.met);
- if (!allRequirementsMet) {
- return;
- }
-
- setLoading(true);
- setSuccess(null);
-
- // For now, all non-employer registrations go through candidate creation
- // This would need to be updated when employer APIs are available
- let success;
- switch (registerForm.userType) {
- case 'candidate':
- success = await createCandidateAccount(registerForm);
- break;
- }
-
- if (success) {
- // Redirect based on user type
- if (registerForm.userType === 'candidate') {
- window.location.href = '/candidate/dashboard';
- }
- setLoading(false);
- }
- };
-
const handleTabChange = (event: React.SyntheticEvent, newValue: number) => {
setTabValue(newValue);
setSuccess(null);
};
- // Toggle password visibility functions
- const toggleLoginPasswordVisibility = () => setShowLoginPassword(!showLoginPassword);
- const toggleRegisterPasswordVisibility = () => setShowRegisterPassword(!showRegisterPassword);
- const toggleConfirmPasswordVisibility = () => setShowConfirmPassword(!showConfirmPassword);
-
- // Get user type icon and description
- const getUserTypeInfo = (userType: UserRegistrationType) => {
- switch (userType) {
- case 'candidate':
- return {
- icon: ,
- title: 'Candidate',
- description: 'Use Backstory to generate your resume and help you manage your career. Optionally let people interact with your profile.'
- };
- case 'employer':
- return {
- icon: ,
- title: 'Employer',
- description: 'Post jobs and find talent (Coming Soon.)'
- };
- }
- };
-
// If user is logged in, show their profile
if (user) {
return (
@@ -356,24 +126,6 @@ const LoginPage: React.FC = (props: BackstoryPageProps) => {
);
}
- const validateInput = (value: string) => {
- if (!value) return 'This field is required';
-
- // Username: alphanumeric, 3-20 characters, no @
- const usernameRegex = /^[a-zA-Z0-9]{3,20}$/;
- // Email: basic email format
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
-
- if (usernameRegex.test(value)) return '';
- if (emailRegex.test(value)) return '';
- return 'Enter a valid username (3-20 alphanumeric characters) or email';
- };
-
- const handleLoginChange = (event: React.ChangeEvent) => {
- const { value } = event.target;
- setLoginForm({ ...loginForm, login: value });
- };
-
return (
@@ -416,328 +168,10 @@ const LoginPage: React.FC = (props: BackstoryPageProps) => {
{tabValue === 0 && (
- //
- //
- // Sign In
- //
-
- //
-
- // setLoginForm({ ...loginForm, password: e.target.value })}
- // margin="normal"
- // required
- // disabled={loading}
- // variant="outlined"
- // autoComplete='current-password'
- // slotProps={{
- // input: {
- // endAdornment: (
- //
- //
- // {showLoginPassword ? : }
- //
- //
- // )
- // }
- // }}
- // />
-
- // : }
- // >
- // {loading ? 'Signing In...' : 'Sign In'}
- //
- //
)}
{tabValue === 1 && (
- //
- //
- // Create Account
- //
-
- // {/* User Type Selection */}
- //
- //
- // Select Account Type
- //
- //
- // {(['candidate', 'employer'] as UserRegistrationType[]).map((userType) => {
- // const info = getUserTypeInfo(userType);
- // return (
- // }
- // label={
- //
- // {info.icon}
- //
- //
- // {info.title}
- // {userType === 'employer' && (
- //
- // )}
- //
- //
- // {info.description}
- //
- //
- //
- // }
- // sx={{
- // border: '1px solid',
- // borderColor: registerForm.userType === userType ? 'primary.main' : 'divider',
- // borderRadius: 1,
- // p: 1,
- // m: 0,
- // bgcolor: registerForm.userType === userType ? 'primary.50' : 'transparent',
- // '&:hover': {
- // bgcolor: userType === 'employer' ? 'grey.100' : 'action.hover'
- // },
- // opacity: userType === 'employer' ? 0.6 : 1
- // }}
- // />
- // );
- // })}
- //
- //
-
- // {/* Employer Placeholder */}
- // {registerForm.userType === 'employer' && (
- //
- //
- // Employer Registration Coming Soon
- //
- //
- // We're currently building our employer features. If you're interested in posting jobs
- // and finding talent, please contact our support team at support@backstory.com for
- // early access.
- //
- //
- // )}
-
- // {/* Basic Information Fields */}
- // {registerForm.userType !== 'employer' && (
- // <>
- //
- //
- // setRegisterForm({ ...registerForm, firstName: e.target.value })}
- // required
- // disabled={loading}
- // variant="outlined"
- // />
- //
-
- //
- // setRegisterForm({ ...registerForm, lastName: e.target.value })}
- // required
- // disabled={loading}
- // variant="outlined"
- // />
- //
- //
-
- // setRegisterForm({ ...registerForm, username: e.target.value })}
- // margin="normal"
- // required
- // disabled={loading}
- // variant="outlined"
- // />
-
- // setRegisterForm({ ...registerForm, email: e.target.value })}
- // margin="normal"
- // required
- // disabled={loading}
- // variant="outlined"
- // />
-
- // {/* Conditional fields based on user type */}
- // {registerForm.userType === 'candidate' && (
- // <>
- // setPhone(v as E164Number)}
- // />
-
- //
- // >
- // )}
-
- // handlePasswordChange(e.target.value)}
- // margin="normal"
- // required
- // disabled={loading}
- // variant="outlined"
- // slotProps={{
- // input: {
- // endAdornment: (
- //
- //
- // {showRegisterPassword ? : }
- //
- //
- // )
- // }
- // }}
- // />
-
- // {/* Password Requirements */}
- // {registerForm.password.length > 0 && (
- //
- //
- //
- //
- //
- // {passwordRequirements.map((requirement, index) => (
- //
- //
- // {requirement.met ? (
- //
- // ) : (
- //
- // )}
- //
- //
- //
- // ))}
- //
- //
- //
- //
- // )}
-
- // setRegisterForm({ ...registerForm, confirmPassword: e.target.value })}
- // margin="normal"
- // required
- // disabled={loading}
- // variant="outlined"
- // error={hasPasswordMatchError}
- // helperText={hasPasswordMatchError ? 'Passwords do not match' : ''}
- // slotProps={{
- // input: {
- // endAdornment: (
- //
- //
- // {showConfirmPassword ? : }
- //
- //
- // )
- // }
- // }}
- // />
-
- //
- // >
- // )}
- //
)}
diff --git a/src/backend/email_service.py b/src/backend/email_service.py
index 156adfe..46e1e22 100644
--- a/src/backend/email_service.py
+++ b/src/backend/email_service.py
@@ -46,7 +46,7 @@ class EmailService:
"""Send email verification email using template"""
try:
template = self._get_template("verification")
- verification_link = f"{self.frontend_url}/verify-email?token={verification_token}"
+ verification_link = f"{self.frontend_url}/login/verify-email?token={verification_token}"
subject = self._format_template(
template["subject"],
@@ -110,7 +110,7 @@ class EmailService:
"""Send password reset email using template"""
try:
template = self._get_template("password_reset")
- reset_link = f"{self.frontend_url}/reset-password?token={reset_token}"
+ reset_link = f"{self.frontend_url}/login/reset-password?token={reset_token}"
subject = self._format_template(template["subject"])
diff --git a/src/backend/email_templates.py b/src/backend/email_templates.py
index 7c81b79..3f714c3 100644
--- a/src/backend/email_templates.py
+++ b/src/backend/email_templates.py
@@ -124,7 +124,7 @@ EMAIL_TEMPLATES = {