brackt/app/routes/admin.sports.new.tsx

156 lines
4.9 KiB
TypeScript
Raw Normal View History

import { Form, Link, redirect } from "react-router";
import type { Route } from "./+types/admin.sports.new";
import { createSport } from "~/models/sport";
import { Button } from "~/components/ui/button";
import { Input } from "~/components/ui/input";
import { Label } from "~/components/ui/label";
import { Textarea } from "~/components/ui/textarea";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "~/components/ui/card";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "~/components/ui/select";
export async function action({ request }: Route.ActionArgs) {
const formData = await request.formData();
const name = formData.get("name");
const type = formData.get("type");
const slug = formData.get("slug");
const description = formData.get("description");
// Validation
if (typeof name !== "string" || !name.trim()) {
return { error: "Sport name is required" };
}
if (type !== "team" && type !== "individual") {
return { error: "Sport type must be either 'team' or 'individual'" };
}
if (typeof slug !== "string" || !slug.trim()) {
return { error: "Slug is required" };
}
// Validate slug format (lowercase, hyphens only)
if (!/^[a-z0-9-]+$/.test(slug)) {
return { error: "Slug must contain only lowercase letters, numbers, and hyphens" };
}
try {
await createSport({
name: name.trim(),
type,
slug: slug.trim(),
description: typeof description === "string" ? description.trim() : null,
});
return redirect("/admin/sports");
} catch (error) {
console.error("Error creating sport:", error);
return { error: "Failed to create sport. The slug might already exist." };
}
}
export default function NewSport({ actionData }: Route.ComponentProps) {
return (
<div className="p-8">
<div className="max-w-2xl">
<div className="mb-6">
<h1 className="text-3xl font-bold">Create New Sport</h1>
<p className="text-muted-foreground mt-1">
Add a new sport to the system
</p>
</div>
<Card>
<CardHeader>
<CardTitle>Sport Details</CardTitle>
<CardDescription>
Enter the information for the new sport
</CardDescription>
</CardHeader>
<CardContent>
<Form method="post" className="space-y-6">
<div className="space-y-2">
<Label htmlFor="name">Sport Name</Label>
<Input
id="name"
name="name"
type="text"
placeholder="e.g., NFL, NBA, Golf - Men's"
required
/>
</div>
<div className="space-y-2">
<Label htmlFor="type">Type</Label>
<Select name="type" required>
<SelectTrigger id="type">
<SelectValue placeholder="Select sport type" />
</SelectTrigger>
<SelectContent>
<SelectItem value="team">Team Sport</SelectItem>
<SelectItem value="individual">Individual Sport</SelectItem>
</SelectContent>
</Select>
<p className="text-sm text-muted-foreground">
Team sports have teams, individual sports have players
</p>
</div>
<div className="space-y-2">
<Label htmlFor="slug">Slug</Label>
<Input
id="slug"
name="slug"
type="text"
placeholder="e.g., nfl, nba, golf-mens"
pattern="[a-z0-9-]+"
required
/>
<p className="text-sm text-muted-foreground">
URL-friendly identifier (lowercase, hyphens only)
</p>
</div>
<div className="space-y-2">
<Label htmlFor="description">Description (Optional)</Label>
<Textarea
id="description"
name="description"
placeholder="Brief description of the sport"
rows={3}
/>
</div>
{actionData?.error && (
<div className="bg-destructive/15 text-destructive px-4 py-3 rounded-md text-sm">
{actionData.error}
</div>
)}
<div className="flex gap-4">
<Button type="submit" className="flex-1">
Create Sport
</Button>
<Button type="button" variant="outline" asChild>
<Link to="/admin/sports">Cancel</Link>
</Button>
</div>
</Form>
</CardContent>
</Card>
</div>
</div>
);
}