diff --git a/app/routes/admin.sports-seasons.$id.participants.tsx b/app/routes/admin.sports-seasons.$id.participants.tsx index 70d2c29..2143e5f 100644 --- a/app/routes/admin.sports-seasons.$id.participants.tsx +++ b/app/routes/admin.sports-seasons.$id.participants.tsx @@ -1,3 +1,4 @@ +import { useState, useEffect } from "react"; import { Form, Link } from "react-router"; import type { Route } from "./+types/admin.sports-seasons.$id.participants"; @@ -8,6 +9,7 @@ import { findParticipantByName, createParticipant, deleteParticipant, + updateParticipant, } from "~/models/participant"; import { Button } from "~/components/ui/button"; import { Input } from "~/components/ui/input"; @@ -28,7 +30,7 @@ import { TableHeader, TableRow, } from "~/components/ui/table"; -import { Plus, Trash2, ArrowLeft } from "lucide-react"; +import { Plus, Trash2, ArrowLeft, Pencil, Check, X } from "lucide-react"; export function meta({ data }: Route.MetaArgs): Route.MetaDescriptors { return [{ title: `Participants — ${data?.sportsSeason?.name ?? "Sports Season"} - Brackt Admin` }]; @@ -62,6 +64,33 @@ export async function action({ request, params }: Route.ActionArgs) { return { success: true }; } + if (intent === "update-name") { + const participantId = formData.get("participantId"); + const newName = formData.get("newName"); + + if (typeof participantId !== "string") { + return { error: "Invalid participant", intent: "update-name" }; + } + + if (typeof newName !== "string" || !newName.trim()) { + return { error: "Name is required", intent: "update-name" }; + } + + const trimmedName = newName.trim(); + const existing = await findParticipantByName(params.id, trimmedName); + if (existing && existing.id !== participantId) { + return { error: `"${trimmedName}" already exists in this season.`, intent: "update-name" }; + } + + try { + await updateParticipant(participantId, { name: trimmedName }); + return { success: true, intent: "update-name" }; + } catch (error) { + logger.error("Error updating participant:", error); + return { error: "Failed to update participant. Please try again.", intent: "update-name" }; + } + } + // Bulk add participants if (intent === "bulk") { const bulkNames = formData.get("bulkNames"); @@ -142,10 +171,19 @@ export async function action({ request, params }: Route.ActionArgs) { export default function ManageParticipants({ loaderData, actionData }: Route.ComponentProps) { const { sportsSeason, participants } = loaderData; - + const [editing, setEditing] = useState<{ id: string; name: string } | null>(null); + + const cancelEdit = () => setEditing(null); + // Use success state to reset forms by changing the key const formKey = actionData?.success ? Date.now() : 'static'; + useEffect(() => { + if (actionData?.success && actionData?.intent === "update-name") { + cancelEdit(); + } + }, [actionData]); + return (
@@ -183,7 +221,7 @@ export default function ManageParticipants({ loaderData, actionData }: Route.Com />
- {actionData?.error && !actionData?.count && ( + {actionData?.error && !actionData?.count && actionData?.intent !== "update-name" && (
{actionData.error}
@@ -229,7 +267,7 @@ export default function ManageParticipants({ loaderData, actionData }: Route.Com

- {actionData?.error && actionData?.count === undefined && ( + {actionData?.error && actionData?.count === undefined && actionData?.intent !== "update-name" && (
{actionData.error}
@@ -273,26 +311,81 @@ export default function ManageParticipants({ loaderData, actionData }: Route.Com Name - + {participants.map((participant) => ( - {participant.name} + + {editing?.id === participant.id ? ( +
+ setEditing({ id: participant.id, name: e.target.value })} + className="h-8" + autoFocus + onKeyDown={(e) => { if (e.key === "Escape") cancelEdit(); }} + /> + {actionData?.error && actionData?.intent === "update-name" && ( +

{actionData.error}

+ )} +
+ ) : ( + participant.name + )} +
-
- - - -
+ {editing?.id === participant.id ? ( +
+
+ + + + +
+ +
+ ) : ( +
+ +
+ + + +
+
+ )}
))}