alpha wip
This commit is contained in:
195
apps/website/components/alpha/CreateLeagueForm.tsx
Normal file
195
apps/website/components/alpha/CreateLeagueForm.tsx
Normal file
@@ -0,0 +1,195 @@
|
||||
'use client';
|
||||
|
||||
import { useState, FormEvent } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import Input from '../ui/Input';
|
||||
import Button from '../ui/Button';
|
||||
import DataWarning from './DataWarning';
|
||||
import { League } from '../../domain/entities/League';
|
||||
import { getLeagueRepository, getDriverRepository } from '../../lib/di-container';
|
||||
|
||||
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 handleSubmit = async (e: FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (loading) return;
|
||||
|
||||
if (!validateForm()) return;
|
||||
|
||||
setLoading(true);
|
||||
|
||||
try {
|
||||
const driverRepo = getDriverRepository();
|
||||
const drivers = await driverRepo.findAll();
|
||||
const currentDriver = drivers[0];
|
||||
|
||||
if (!currentDriver) {
|
||||
setErrors({ submit: 'No driver profile found. Please create a profile first.' });
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const leagueRepo = getLeagueRepository();
|
||||
|
||||
const league = League.create({
|
||||
id: crypto.randomUUID(),
|
||||
name: formData.name.trim(),
|
||||
description: formData.description.trim(),
|
||||
ownerId: currentDriver.id,
|
||||
settings: {
|
||||
pointsSystem: formData.pointsSystem,
|
||||
sessionDuration: formData.sessionDuration,
|
||||
qualifyingFormat: 'open',
|
||||
},
|
||||
});
|
||||
|
||||
await leagueRepo.create(league);
|
||||
router.push(`/leagues/${league.id}`);
|
||||
router.refresh();
|
||||
} catch (error) {
|
||||
setErrors({
|
||||
submit: error instanceof Error ? error.message : 'Failed to create league'
|
||||
});
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<DataWarning />
|
||||
<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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user