283 lines
10 KiB
JavaScript
283 lines
10 KiB
JavaScript
import React, { useState } from 'react';
|
|
import { useNavigate } from 'react-router-dom';
|
|
import { useAuth } from '../contexts/AuthContext';
|
|
import { authAPI } from '../services/api';
|
|
import { EyeIcon, EyeSlashIcon } from '@heroicons/react/24/outline';
|
|
import LoadingSpinner from '../components/LoadingSpinner';
|
|
import toast from 'react-hot-toast';
|
|
|
|
const Setup = () => {
|
|
const [formData, setFormData] = useState({
|
|
firstName: '',
|
|
lastName: '',
|
|
email: '',
|
|
password: '',
|
|
confirmPassword: '',
|
|
phone: ''
|
|
});
|
|
const [showPassword, setShowPassword] = useState(false);
|
|
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
|
|
const [loading, setLoading] = useState(false);
|
|
const [error, setError] = useState('');
|
|
|
|
const { updateUser } = useAuth();
|
|
const navigate = useNavigate();
|
|
|
|
const handleChange = (e) => {
|
|
setFormData({
|
|
...formData,
|
|
[e.target.name]: e.target.value
|
|
});
|
|
};
|
|
|
|
const handleSubmit = async (e) => {
|
|
e.preventDefault();
|
|
setLoading(true);
|
|
setError('');
|
|
|
|
// Validation
|
|
if (formData.password !== formData.confirmPassword) {
|
|
setError('Passwords do not match');
|
|
setLoading(false);
|
|
return;
|
|
}
|
|
|
|
if (formData.password.length < 6) {
|
|
setError('Password must be at least 6 characters long');
|
|
setLoading(false);
|
|
return;
|
|
}
|
|
|
|
if (!formData.phone || formData.phone.trim() === '') {
|
|
setError('Phone number is required');
|
|
setLoading(false);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const response = await authAPI.setup({
|
|
firstName: formData.firstName,
|
|
lastName: formData.lastName,
|
|
email: formData.email,
|
|
password: formData.password,
|
|
phone: formData.phone || null
|
|
});
|
|
|
|
const { token, user } = response.data;
|
|
|
|
console.log('Setup successful, user:', user);
|
|
console.log('About to update user in auth context...');
|
|
|
|
// Store token and set user
|
|
localStorage.setItem('token', token);
|
|
|
|
// Update the auth context user state directly
|
|
updateUser(user);
|
|
|
|
console.log('User updated in auth context, about to redirect...');
|
|
|
|
toast.success('System setup completed successfully!');
|
|
|
|
// Redirect to dashboard
|
|
setTimeout(() => {
|
|
console.log('Redirecting to dashboard...');
|
|
navigate('/dashboard');
|
|
}, 1000);
|
|
|
|
} catch (error) {
|
|
console.error('Setup error:', error);
|
|
const message = error.response?.data?.error || 'Setup failed';
|
|
setError(message);
|
|
toast.error(message);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="min-h-screen flex items-center justify-center bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
|
|
<div className="max-w-md w-full space-y-8">
|
|
<div>
|
|
<div className="mx-auto h-12 w-12 flex items-center justify-center rounded-full bg-primary-100">
|
|
<svg
|
|
className="h-8 w-8 text-primary-600"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<path
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
strokeWidth={2}
|
|
d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.746 0 3.332.477 4.5 1.253v13C19.832 18.477 18.246 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"
|
|
/>
|
|
</svg>
|
|
</div>
|
|
<h2 className="mt-6 text-center text-3xl font-extrabold text-gray-900">
|
|
CourseWorx Setup
|
|
</h2>
|
|
<p className="mt-2 text-center text-sm text-gray-600">
|
|
Create your Super Admin account to get started
|
|
</p>
|
|
</div>
|
|
|
|
<form className="mt-8 space-y-6" onSubmit={handleSubmit}>
|
|
<div className="space-y-4">
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<label htmlFor="firstName" className="block text-sm font-medium text-gray-700">
|
|
First Name
|
|
</label>
|
|
<input
|
|
id="firstName"
|
|
name="firstName"
|
|
type="text"
|
|
required
|
|
className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500"
|
|
value={formData.firstName}
|
|
onChange={handleChange}
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label htmlFor="lastName" className="block text-sm font-medium text-gray-700">
|
|
Last Name
|
|
</label>
|
|
<input
|
|
id="lastName"
|
|
name="lastName"
|
|
type="text"
|
|
required
|
|
className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500"
|
|
value={formData.lastName}
|
|
onChange={handleChange}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<label htmlFor="email" className="block text-sm font-medium text-gray-700">
|
|
Email Address
|
|
</label>
|
|
<input
|
|
id="email"
|
|
name="email"
|
|
type="email"
|
|
required
|
|
className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500"
|
|
value={formData.email}
|
|
onChange={handleChange}
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label htmlFor="phone" className="block text-sm font-medium text-gray-700">
|
|
Phone Number <span className="text-red-500">*</span>
|
|
</label>
|
|
<input
|
|
id="phone"
|
|
name="phone"
|
|
type="tel"
|
|
required
|
|
className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500"
|
|
value={formData.phone}
|
|
onChange={handleChange}
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label htmlFor="password" className="block text-sm font-medium text-gray-700">
|
|
Password
|
|
</label>
|
|
<div className="relative">
|
|
<input
|
|
id="password"
|
|
name="password"
|
|
type={showPassword ? 'text' : 'password'}
|
|
required
|
|
className="mt-1 block w-full px-3 py-2 pr-10 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500"
|
|
value={formData.password}
|
|
onChange={handleChange}
|
|
/>
|
|
<button
|
|
type="button"
|
|
className="absolute inset-y-0 right-0 pr-3 flex items-center"
|
|
onClick={() => setShowPassword(!showPassword)}
|
|
>
|
|
{showPassword ? (
|
|
<EyeSlashIcon className="h-5 w-5 text-gray-400" />
|
|
) : (
|
|
<EyeIcon className="h-5 w-5 text-gray-400" />
|
|
)}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<label htmlFor="confirmPassword" className="block text-sm font-medium text-gray-700">
|
|
Confirm Password
|
|
</label>
|
|
<div className="relative">
|
|
<input
|
|
id="confirmPassword"
|
|
name="confirmPassword"
|
|
type={showConfirmPassword ? 'text' : 'password'}
|
|
required
|
|
className="mt-1 block w-full px-3 py-2 pr-10 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500"
|
|
value={formData.confirmPassword}
|
|
onChange={handleChange}
|
|
/>
|
|
<button
|
|
type="button"
|
|
className="absolute inset-y-0 right-0 pr-3 flex items-center"
|
|
onClick={() => setShowConfirmPassword(!showConfirmPassword)}
|
|
>
|
|
{showConfirmPassword ? (
|
|
<EyeSlashIcon className="h-5 w-5 text-gray-400" />
|
|
) : (
|
|
<EyeIcon className="h-5 w-5 text-gray-400" />
|
|
)}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{error && (
|
|
<div className="rounded-md bg-red-50 p-4 border border-red-200">
|
|
<div className="flex">
|
|
<div className="flex-shrink-0">
|
|
<svg className="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor">
|
|
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clipRule="evenodd" />
|
|
</svg>
|
|
</div>
|
|
<div className="ml-3">
|
|
<h3 className="text-sm font-medium text-red-800">
|
|
Setup Error
|
|
</h3>
|
|
<div className="mt-2 text-sm text-red-700">
|
|
{error}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
<div>
|
|
<button
|
|
type="submit"
|
|
disabled={loading}
|
|
className="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-primary-600 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
>
|
|
{loading ? (
|
|
<LoadingSpinner size="sm" />
|
|
) : (
|
|
'Complete Setup'
|
|
)}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default Setup;
|