Simplify edit state and surface edit errors to user
- Combine editingId + editingName into single editing: { id, name } | null state object
- Extract cancelEdit() helper called in 3 places instead of repeating setEditing(null)
- Tag all update-name action responses with intent: "update-name" so errors can be
identified and routed correctly
- Show update-name errors inline below the edit input
- Guard single-add and bulk-add error displays against showing update-name errors
https://claude.ai/code/session_01Q94DWQ1HZEB9PJhGMArU8D
This commit is contained in:
parent
e43fd17de4
commit
93fe319830
1 changed files with 27 additions and 33 deletions
|
|
@ -69,17 +69,17 @@ export async function action({ request, params }: Route.ActionArgs) {
|
|||
const newName = formData.get("newName");
|
||||
|
||||
if (typeof participantId !== "string") {
|
||||
return { error: "Invalid participant" };
|
||||
return { error: "Invalid participant", intent: "update-name" };
|
||||
}
|
||||
|
||||
if (typeof newName !== "string" || !newName.trim()) {
|
||||
return { error: "Name is required" };
|
||||
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.` };
|
||||
return { error: `"${trimmedName}" already exists in this season.`, intent: "update-name" };
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
@ -87,7 +87,7 @@ export async function action({ request, params }: Route.ActionArgs) {
|
|||
return { success: true, intent: "update-name" };
|
||||
} catch (error) {
|
||||
logger.error("Error updating participant:", error);
|
||||
return { error: "Failed to update participant. Please try again." };
|
||||
return { error: "Failed to update participant. Please try again.", intent: "update-name" };
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -171,16 +171,16 @@ export async function action({ request, params }: Route.ActionArgs) {
|
|||
|
||||
export default function ManageParticipants({ loaderData, actionData }: Route.ComponentProps) {
|
||||
const { sportsSeason, participants } = loaderData;
|
||||
const [editingId, setEditingId] = useState<string | null>(null);
|
||||
const [editingName, setEditingName] = useState("");
|
||||
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") {
|
||||
setEditingId(null);
|
||||
setEditingName("");
|
||||
cancelEdit();
|
||||
}
|
||||
}, [actionData]);
|
||||
|
||||
|
|
@ -221,7 +221,7 @@ export default function ManageParticipants({ loaderData, actionData }: Route.Com
|
|||
/>
|
||||
</div>
|
||||
|
||||
{actionData?.error && !actionData?.count && (
|
||||
{actionData?.error && !actionData?.count && actionData?.intent !== "update-name" && (
|
||||
<div className="bg-destructive/15 text-destructive px-4 py-3 rounded-md text-sm">
|
||||
{actionData.error}
|
||||
</div>
|
||||
|
|
@ -267,7 +267,7 @@ export default function ManageParticipants({ loaderData, actionData }: Route.Com
|
|||
</p>
|
||||
</div>
|
||||
|
||||
{actionData?.error && actionData?.count === undefined && (
|
||||
{actionData?.error && actionData?.count === undefined && actionData?.intent !== "update-name" && (
|
||||
<div className="bg-destructive/15 text-destructive px-4 py-3 rounded-md text-sm">
|
||||
{actionData.error}
|
||||
</div>
|
||||
|
|
@ -318,30 +318,30 @@ export default function ManageParticipants({ loaderData, actionData }: Route.Com
|
|||
{participants.map((participant) => (
|
||||
<TableRow key={participant.id}>
|
||||
<TableCell className="font-medium">
|
||||
{editingId === participant.id ? (
|
||||
<Input
|
||||
value={editingName}
|
||||
onChange={(e) => setEditingName(e.target.value)}
|
||||
className="h-8"
|
||||
autoFocus
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Escape") {
|
||||
setEditingId(null);
|
||||
setEditingName("");
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{editing?.id === participant.id ? (
|
||||
<div className="space-y-1">
|
||||
<Input
|
||||
value={editing.name}
|
||||
onChange={(e) => 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" && (
|
||||
<p className="text-xs text-destructive">{actionData.error}</p>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
participant.name
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{editingId === participant.id ? (
|
||||
{editing?.id === participant.id ? (
|
||||
<div className="flex gap-1">
|
||||
<Form method="post">
|
||||
<input type="hidden" name="intent" value="update-name" />
|
||||
<input type="hidden" name="participantId" value={participant.id} />
|
||||
<input type="hidden" name="newName" value={editingName} />
|
||||
<input type="hidden" name="newName" value={editing.name} />
|
||||
<Button
|
||||
type="submit"
|
||||
variant="ghost"
|
||||
|
|
@ -356,10 +356,7 @@ export default function ManageParticipants({ loaderData, actionData }: Route.Com
|
|||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-8 w-8 p-0"
|
||||
onClick={() => {
|
||||
setEditingId(null);
|
||||
setEditingName("");
|
||||
}}
|
||||
onClick={cancelEdit}
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</Button>
|
||||
|
|
@ -371,10 +368,7 @@ export default function ManageParticipants({ loaderData, actionData }: Route.Com
|
|||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-8 w-8 p-0"
|
||||
onClick={() => {
|
||||
setEditingId(participant.id);
|
||||
setEditingName(participant.name);
|
||||
}}
|
||||
onClick={() => setEditing({ id: participant.id, name: participant.name })}
|
||||
>
|
||||
<Pencil className="h-4 w-4" />
|
||||
</Button>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue