194 lines
6.1 KiB
TypeScript
194 lines
6.1 KiB
TypeScript
'use client';
|
|
|
|
import { useState, FormEvent } from 'react';
|
|
import { useRouter } from 'next/navigation';
|
|
import Input from '../ui/Input';
|
|
import Button from '../ui/Button';
|
|
import { useServices } from '@/lib/services/ServiceProvider';
|
|
import { useAuth } from '@/lib/auth/AuthContext';
|
|
|
|
interface FormErrors {
|
|
name?: string;
|
|
description?: string;
|
|
pointsSystem?: string;
|
|
sessionDuration?: string;
|
|
submit?: string;
|
|
}
|
|
|
|
export default function CreateLeagueForm() {
|
|
const router = useRouter();
|
|
const [loading, setLoading] = useState(false);
|
|
const [errors, setErrors] = useState<FormErrors>({});
|
|
|
|
const [formData, setFormData] = useState({
|
|
name: '',
|
|
description: '',
|
|
pointsSystem: 'f1-2024' as 'f1-2024' | 'indycar',
|
|
sessionDuration: 60
|
|
});
|
|
|
|
const validateForm = (): boolean => {
|
|
const newErrors: FormErrors = {};
|
|
|
|
if (!formData.name.trim()) {
|
|
newErrors.name = 'Name is required';
|
|
} else if (formData.name.length > 100) {
|
|
newErrors.name = 'Name must be 100 characters or less';
|
|
}
|
|
|
|
if (!formData.description.trim()) {
|
|
newErrors.description = 'Description is required';
|
|
} else if (formData.description.length > 500) {
|
|
newErrors.description = 'Description must be 500 characters or less';
|
|
}
|
|
|
|
if (formData.sessionDuration < 1 || formData.sessionDuration > 240) {
|
|
newErrors.sessionDuration = 'Session duration must be between 1 and 240 minutes';
|
|
}
|
|
|
|
setErrors(newErrors);
|
|
return Object.keys(newErrors).length === 0;
|
|
};
|
|
|
|
const { session } = useAuth();
|
|
const { driverService, leagueService } = useServices();
|
|
|
|
const handleSubmit = async (e: FormEvent) => {
|
|
e.preventDefault();
|
|
|
|
if (loading) return;
|
|
|
|
if (!validateForm()) return;
|
|
|
|
if (!session?.user.userId) {
|
|
setErrors({ submit: 'You must be logged in to create a league' });
|
|
return;
|
|
}
|
|
|
|
setLoading(true);
|
|
|
|
try {
|
|
// Get current driver
|
|
const currentDriver = await driverService.getDriverProfile(session.user.userId);
|
|
|
|
if (!currentDriver) {
|
|
setErrors({ submit: 'No driver profile found. Please create a profile first.' });
|
|
setLoading(false);
|
|
return;
|
|
}
|
|
|
|
// Create league using the league service
|
|
const input = {
|
|
name: formData.name.trim(),
|
|
description: formData.description.trim(),
|
|
visibility: 'public' as const,
|
|
ownerId: session.user.userId,
|
|
};
|
|
|
|
const result = await leagueService.createLeague(input);
|
|
router.push(`/leagues/${result.leagueId}`);
|
|
router.refresh();
|
|
} catch (error) {
|
|
setErrors({
|
|
submit: error instanceof Error ? error.message : 'Failed to create league'
|
|
});
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<form onSubmit={handleSubmit} className="space-y-6">
|
|
<div>
|
|
<label htmlFor="name" className="block text-sm font-medium text-gray-300 mb-2">
|
|
League Name *
|
|
</label>
|
|
<Input
|
|
id="name"
|
|
type="text"
|
|
value={formData.name}
|
|
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
|
error={!!errors.name}
|
|
errorMessage={errors.name}
|
|
placeholder="European GT Championship"
|
|
maxLength={100}
|
|
disabled={loading}
|
|
/>
|
|
<p className="mt-1 text-xs text-gray-500 text-right">
|
|
{formData.name.length}/100
|
|
</p>
|
|
</div>
|
|
|
|
<div>
|
|
<label htmlFor="description" className="block text-sm font-medium text-gray-300 mb-2">
|
|
Description *
|
|
</label>
|
|
<textarea
|
|
id="description"
|
|
value={formData.description}
|
|
onChange={(e) => setFormData({ ...formData, description: e.target.value })}
|
|
placeholder="Weekly GT3 racing with professional drivers"
|
|
maxLength={500}
|
|
rows={4}
|
|
disabled={loading}
|
|
className="block w-full rounded-md border-0 px-4 py-3 bg-iron-gray text-white shadow-sm ring-1 ring-inset ring-charcoal-outline placeholder:text-gray-500 focus:ring-2 focus:ring-inset focus:ring-primary-blue transition-all duration-150 sm:text-sm sm:leading-6 resize-none"
|
|
/>
|
|
<p className="mt-1 text-xs text-gray-500 text-right">
|
|
{formData.description.length}/500
|
|
</p>
|
|
{errors.description && (
|
|
<p className="mt-2 text-sm text-warning-amber">{errors.description}</p>
|
|
)}
|
|
</div>
|
|
|
|
<div>
|
|
<label htmlFor="pointsSystem" className="block text-sm font-medium text-gray-300 mb-2">
|
|
Points System *
|
|
</label>
|
|
<select
|
|
id="pointsSystem"
|
|
value={formData.pointsSystem}
|
|
onChange={(e) => setFormData({ ...formData, pointsSystem: e.target.value as 'f1-2024' | 'indycar' })}
|
|
disabled={loading}
|
|
className="block w-full rounded-md border-0 px-4 py-3 bg-iron-gray text-white shadow-sm ring-1 ring-inset ring-charcoal-outline focus:ring-2 focus:ring-inset focus:ring-primary-blue transition-all duration-150 sm:text-sm sm:leading-6"
|
|
>
|
|
<option value="f1-2024">F1 2024</option>
|
|
<option value="indycar">IndyCar</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label htmlFor="sessionDuration" className="block text-sm font-medium text-gray-300 mb-2">
|
|
Session Duration (minutes) *
|
|
</label>
|
|
<Input
|
|
id="sessionDuration"
|
|
type="number"
|
|
value={formData.sessionDuration}
|
|
onChange={(e) => setFormData({ ...formData, sessionDuration: parseInt(e.target.value) || 60 })}
|
|
error={!!errors.sessionDuration}
|
|
errorMessage={errors.sessionDuration}
|
|
min={1}
|
|
max={240}
|
|
disabled={loading}
|
|
/>
|
|
</div>
|
|
|
|
{errors.submit && (
|
|
<div className="rounded-md bg-warning-amber/10 p-4 border border-warning-amber/20">
|
|
<p className="text-sm text-warning-amber">{errors.submit}</p>
|
|
</div>
|
|
)}
|
|
|
|
<Button
|
|
type="submit"
|
|
variant="primary"
|
|
disabled={loading}
|
|
className="w-full"
|
|
>
|
|
{loading ? 'Creating League...' : 'Create League'}
|
|
</Button>
|
|
</form>
|
|
</>
|
|
);
|
|
} |