Initial commit: SmoothSchedule multi-tenant scheduling platform
This commit includes: - Django backend with multi-tenancy (django-tenants) - React + TypeScript frontend with Vite - Platform administration API with role-based access control - Authentication system with token-based auth - Quick login dev tools for testing different user roles - CORS and CSRF configuration for local development - Docker development environment setup 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,201 @@
|
||||
/**
|
||||
* Email Verification Required Page
|
||||
*
|
||||
* Displayed when a user needs to verify their email address before accessing the application.
|
||||
* Provides options to resend verification email and log out.
|
||||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useCurrentUser } from '../hooks/useAuth';
|
||||
import apiClient from '../api/client';
|
||||
import { useLogout } from '../hooks/useAuth';
|
||||
|
||||
const EmailVerificationRequired: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const { data: user } = useCurrentUser();
|
||||
const logoutMutation = useLogout();
|
||||
const [sending, setSending] = useState(false);
|
||||
const [sent, setSent] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const handleResendEmail = async () => {
|
||||
setSending(true);
|
||||
setError(null);
|
||||
setSent(false);
|
||||
|
||||
try {
|
||||
await apiClient.post('/api/auth/email/verify/send/');
|
||||
setSent(true);
|
||||
setTimeout(() => setSent(false), 5000); // Hide success message after 5 seconds
|
||||
} catch (err: any) {
|
||||
setError(err.response?.data?.detail || 'Failed to send verification email');
|
||||
} finally {
|
||||
setSending(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleLogout = () => {
|
||||
logoutMutation.mutate();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-blue-50 to-indigo-100 dark:from-gray-900 dark:to-gray-800 px-4">
|
||||
<div className="max-w-md w-full bg-white dark:bg-gray-800 rounded-lg shadow-xl p-8">
|
||||
{/* Icon */}
|
||||
<div className="flex justify-center mb-6">
|
||||
<div className="w-20 h-20 bg-amber-100 dark:bg-amber-900/30 rounded-full flex items-center justify-center">
|
||||
<svg
|
||||
className="w-10 h-10 text-amber-600 dark:text-amber-500"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Title */}
|
||||
<h1 className="text-2xl font-bold text-center text-gray-900 dark:text-white mb-2">
|
||||
Email Verification Required
|
||||
</h1>
|
||||
|
||||
{/* Message */}
|
||||
<p className="text-center text-gray-600 dark:text-gray-400 mb-6">
|
||||
Please verify your email address to access your account.
|
||||
</p>
|
||||
|
||||
{/* Email Display */}
|
||||
<div className="bg-gray-50 dark:bg-gray-700 rounded-lg p-4 mb-6">
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400 mb-1">
|
||||
Verification email sent to:
|
||||
</p>
|
||||
<p className="text-base font-medium text-gray-900 dark:text-white break-all">
|
||||
{user?.email}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Instructions */}
|
||||
<div className="bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg p-4 mb-6">
|
||||
<p className="text-sm text-blue-800 dark:text-blue-300">
|
||||
Check your inbox for a verification email and click the link to verify your account.
|
||||
Don't forget to check your spam folder if you don't see it.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Success Message */}
|
||||
{sent && (
|
||||
<div className="bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-lg p-4 mb-4">
|
||||
<p className="text-sm text-green-800 dark:text-green-300 text-center">
|
||||
Verification email sent successfully! Check your inbox.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Error Message */}
|
||||
{error && (
|
||||
<div className="bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg p-4 mb-4">
|
||||
<p className="text-sm text-red-800 dark:text-red-300 text-center">
|
||||
{error}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Actions */}
|
||||
<div className="space-y-3">
|
||||
{/* Resend Email Button */}
|
||||
<button
|
||||
onClick={handleResendEmail}
|
||||
disabled={sending || sent}
|
||||
className="w-full px-4 py-3 bg-blue-600 hover:bg-blue-700 disabled:bg-blue-400 text-white font-medium rounded-lg transition-colors duration-200 flex items-center justify-center gap-2"
|
||||
>
|
||||
{sending ? (
|
||||
<>
|
||||
<svg
|
||||
className="animate-spin h-5 w-5 text-white"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<circle
|
||||
className="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
strokeWidth="4"
|
||||
></circle>
|
||||
<path
|
||||
className="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
></path>
|
||||
</svg>
|
||||
Sending...
|
||||
</>
|
||||
) : sent ? (
|
||||
<>
|
||||
<svg
|
||||
className="h-5 w-5"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
Email Sent
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<svg
|
||||
className="h-5 w-5"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
|
||||
/>
|
||||
</svg>
|
||||
Resend Verification Email
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
|
||||
{/* Logout Button */}
|
||||
<button
|
||||
onClick={handleLogout}
|
||||
className="w-full px-4 py-3 bg-gray-200 hover:bg-gray-300 dark:bg-gray-700 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-300 font-medium rounded-lg transition-colors duration-200"
|
||||
>
|
||||
Log Out
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Help Text */}
|
||||
<p className="text-center text-sm text-gray-500 dark:text-gray-400 mt-6">
|
||||
Need help? Contact support at{' '}
|
||||
<a
|
||||
href="mailto:support@smoothschedule.com"
|
||||
className="text-blue-600 dark:text-blue-400 hover:underline"
|
||||
>
|
||||
support@smoothschedule.com
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default EmailVerificationRequired;
|
||||
Reference in New Issue
Block a user