courseworx/frontend/src/pages/Setup.js

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;