diff --git a/backend/api/views.py b/backend/api/views.py
index 23fd3d8..990b095 100644
--- a/backend/api/views.py
+++ b/backend/api/views.py
@@ -323,7 +323,40 @@ class UserViewSet(viewsets.ModelViewSet):
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)
- return Response({'status': 'User created, please verify your email'}, status=status.HTTP_201_CREATED)
+ return Response({'status': 'User created, please verify your email', 'username': username, 'email': email}, status=status.HTTP_201_CREATED)
+
+ @action(detail=False, methods=['post'], permission_classes=[permissions.AllowAny])
+ def resend_verification(self, request):
+ username = request.data.get('username')
+ if not username:
+ return Response({'error': 'Username required'}, status=status.HTTP_400_BAD_REQUEST)
+
+ try:
+ user = User.objects.get(username=username, is_active=False)
+ except User.DoesNotExist:
+ return Response({'error': 'User not found or already active'}, status=status.HTTP_404_NOT_FOUND)
+
+ import secrets
+ import string
+ code = ''.join(secrets.choice(string.digits) for _ in range(6))
+ user.email_confirmation_code = code
+ user.save()
+
+ try:
+ send_mail(
+ 'Verify your Holy Flavors account',
+ f'Hi {user.username},\n\nYour new verification code is: {code}',
+ settings.DEFAULT_FROM_EMAIL,
+ [user.email],
+ fail_silently=False,
+ )
+ except Exception as e:
+ return Response(
+ {'error': f'Failed to send email: {e}'},
+ status=status.HTTP_500_INTERNAL_SERVER_ERROR
+ )
+
+ return Response({'status': 'Verification code resent!'})
@action(detail=False, methods=['post'], permission_classes=[permissions.AllowAny])
def verify_signup(self, request):
diff --git a/frontend/src/i18n.ts b/frontend/src/i18n.ts
index b71a800..e98987e 100644
--- a/frontend/src/i18n.ts
+++ b/frontend/src/i18n.ts
@@ -118,6 +118,20 @@ const resources = {
"save": "Save",
"cancel": "Cancel"
},
+ "auth": {
+ "loginTitle": "Login",
+ "signupTitle": "Signup",
+ "verifyTitle": "Verify Account",
+ "verifyDesc": "Enter the code sent to your email for {{username}}.",
+ "resendButton": "Resend Verification Code",
+ "resendSuccess": "A new verification code has been sent to your email.",
+ "verifySuccess": "Account verified! You can now login.",
+ "unverifiedError": "Account not verified. Please verify your account to login.",
+ "loginFailed": "Login failed. Please check your credentials.",
+ "signupSuccess": "Signup successful! Please enter the verification code sent to your email.",
+ "forgotPassword": "Forgot Password?",
+ "backToAuth": "Back to Signup/Login"
+ },
"community": {
"title": "Community Activity",
"subtitle": "Stay updated with your circle's latest ratings and discussions.",
@@ -344,6 +358,20 @@ const resources = {
"save": "Speichern",
"cancel": "Abbrechen"
},
+ "auth": {
+ "loginTitle": "Anmelden",
+ "signupTitle": "Registrieren",
+ "verifyTitle": "Konto verifizieren",
+ "verifyDesc": "Gib den Code ein, der an deine E-Mail für {{username}} gesendet wurde.",
+ "resendButton": "Verifizierungscode erneut senden",
+ "resendSuccess": "Ein neuer Verifizierungscode wurde an deine E-Mail gesendet.",
+ "verifySuccess": "Konto verifiziert! Du kannst dich jetzt anmelden.",
+ "unverifiedError": "Konto nicht verifiziert. Bitte verifiziere dein Konto, um dich anzumelden.",
+ "loginFailed": "Anmeldung fehlgeschlagen. Bitte überprüfe deine Zugangsdaten.",
+ "signupSuccess": "Registrierung erfolgreich! Bitte gib den Verifizierungscode ein, der an deine E-Mail gesendet wurde.",
+ "forgotPassword": "Passwort vergessen?",
+ "backToAuth": "Zurück zur Anmeldung"
+ },
"community": {
"title": "Community-Aktivität",
"subtitle": "Bleib auf dem Laufenden mit den neuesten Bewertungen und Diskussionen deines Kreises.",
diff --git a/frontend/src/pages/Login.tsx b/frontend/src/pages/Login.tsx
index 0ca40d5..c20a3e1 100644
--- a/frontend/src/pages/Login.tsx
+++ b/frontend/src/pages/Login.tsx
@@ -16,6 +16,7 @@ const Login: React.FC = () => {
const [email, setEmail] = useState('');
const [verificationCode, setVerificationCode] = useState('');
const [isVerifying, setIsVerifying] = useState(false);
+ const [showResend, setShowResend] = useState(false);
const [isResetting, setIsResetting] = useState(false);
const [resetStep, setResetStep] = useState(1); // 1: email, 2: code/new pass
const [message, setMessage] = useState<{type: 'success' | 'error', text: string} | null>(null);
@@ -31,7 +32,13 @@ const Login: React.FC = () => {
localStorage.setItem('token', res.data.token);
window.location.href = '/';
} catch (err: any) {
- setMessage({ type: 'error', text: 'Login failed. Please check your credentials or verify your account.' });
+ const errorMsg = err.response?.data?.non_field_errors?.[0] || t('auth.loginFailed');
+ if (errorMsg.toLowerCase().includes('inactive') || errorMsg.toLowerCase().includes('verify') || errorMsg.includes('disabled')) {
+ setMessage({ type: 'error', text: t('auth.unverifiedError') });
+ setIsVerifying(true);
+ } else {
+ setMessage({ type: 'error', text: errorMsg });
+ }
}
};
@@ -41,7 +48,7 @@ const Login: React.FC = () => {
try {
await api.post('users/signup/', { username, email, password });
setIsVerifying(true);
- setMessage({ type: 'success', text: 'Signup successful! Please enter the verification code sent to your email.' });
+ setMessage({ type: 'success', text: t('auth.signupSuccess') });
} catch (err: any) {
setMessage({ type: 'error', text: err.response?.data?.error || 'Signup failed' });
}
@@ -52,12 +59,24 @@ const Login: React.FC = () => {
setMessage(null);
try {
await api.post('users/verify_signup/', { username, code: verificationCode });
- setMessage({ type: 'success', text: 'Account verified! You can now login.' });
+ setMessage({ type: 'success', text: t('auth.verifySuccess') });
setIsVerifying(false);
setTab(0);
setPassword('');
} catch (err: any) {
setMessage({ type: 'error', text: err.response?.data?.error || 'Verification failed' });
+ setShowResend(true);
+ }
+ };
+
+ const handleResendCode = async () => {
+ setMessage(null);
+ try {
+ await api.post('users/resend_verification/', { username });
+ setMessage({ type: 'success', text: t('auth.resendSuccess') });
+ setShowResend(false);
+ } catch (err: any) {
+ setMessage({ type: 'error', text: err.response?.data?.error || 'Failed to resend code' });
}
};
@@ -93,20 +112,26 @@ const Login: React.FC = () => {
if (isVerifying) {
return (
);
@@ -156,8 +181,8 @@ const Login: React.FC = () => {
return (
<>
{ setTab(v); setMessage(null); }} sx={{ mb: 2 }}>
-
-
+
+
{tab === 0 ? (
@@ -178,10 +203,10 @@ const Login: React.FC = () => {
onChange={(e) => setPassword(e.target.value)}
/>
) : (
@@ -210,7 +235,7 @@ const Login: React.FC = () => {
onChange={(e) => setPassword(e.target.value)}
/>
)}