170 lines
6.3 KiB
TypeScript
170 lines
6.3 KiB
TypeScript
|
|
import type { Dispatch, SetStateAction } from "react";
|
||
|
|
import {
|
||
|
|
closestCenter,
|
||
|
|
DndContext,
|
||
|
|
KeyboardSensor,
|
||
|
|
PointerSensor,
|
||
|
|
useSensor,
|
||
|
|
useSensors,
|
||
|
|
} from "@dnd-kit/core";
|
||
|
|
import {
|
||
|
|
arrayMove,
|
||
|
|
SortableContext,
|
||
|
|
sortableKeyboardCoordinates,
|
||
|
|
verticalListSortingStrategy,
|
||
|
|
} from "@dnd-kit/sortable";
|
||
|
|
import { Form } from "react-router";
|
||
|
|
import { ListOrdered } from "lucide-react";
|
||
|
|
import { Button } from "~/components/ui/button";
|
||
|
|
import { SettingsSection, SettingsStatusPill } from "./SettingsSection";
|
||
|
|
import { DraftOrderMessage, type SettingsActionData } from "./SettingsMessages";
|
||
|
|
import { SortableDraftOrderRow } from "./SortableDraftOrderRow";
|
||
|
|
|
||
|
|
type DraftOrderTeam = {
|
||
|
|
id: string;
|
||
|
|
name: string;
|
||
|
|
ownerId: string | null;
|
||
|
|
};
|
||
|
|
|
||
|
|
export function DraftOrderSection({
|
||
|
|
active,
|
||
|
|
draftOrderSet,
|
||
|
|
canEditDraftOrder,
|
||
|
|
hasUnsavedChanges,
|
||
|
|
draftOrderTeams,
|
||
|
|
setDraftOrderTeams,
|
||
|
|
onDirtyChange,
|
||
|
|
onResetChanges,
|
||
|
|
teams,
|
||
|
|
ownerMap,
|
||
|
|
navigationState,
|
||
|
|
updateIntent,
|
||
|
|
actionData,
|
||
|
|
}: {
|
||
|
|
active: boolean;
|
||
|
|
draftOrderSet: boolean;
|
||
|
|
canEditDraftOrder: boolean;
|
||
|
|
hasUnsavedChanges: boolean;
|
||
|
|
draftOrderTeams: string[];
|
||
|
|
setDraftOrderTeams: Dispatch<SetStateAction<string[]>>;
|
||
|
|
onDirtyChange: (dirty: boolean) => void;
|
||
|
|
onResetChanges: () => void;
|
||
|
|
teams: DraftOrderTeam[];
|
||
|
|
ownerMap: Record<string, string | null>;
|
||
|
|
navigationState: "idle" | "loading" | "submitting";
|
||
|
|
updateIntent: FormDataEntryValue | null | undefined;
|
||
|
|
actionData: SettingsActionData;
|
||
|
|
}) {
|
||
|
|
const sensors = useSensors(
|
||
|
|
useSensor(PointerSensor, {
|
||
|
|
activationConstraint: { distance: 6 },
|
||
|
|
}),
|
||
|
|
useSensor(KeyboardSensor, {
|
||
|
|
coordinateGetter: sortableKeyboardCoordinates,
|
||
|
|
})
|
||
|
|
);
|
||
|
|
|
||
|
|
return (
|
||
|
|
<SettingsSection
|
||
|
|
id="draft-order"
|
||
|
|
icon={ListOrdered}
|
||
|
|
title="Draft Order"
|
||
|
|
description="Drag positions to reorder teams, or randomize the entire draft order. Saved independently from the rest of the settings form."
|
||
|
|
status={draftOrderSet ? <SettingsStatusPill tone="success">Set</SettingsStatusPill> : undefined}
|
||
|
|
className={active ? undefined : "hidden"}
|
||
|
|
>
|
||
|
|
{!draftOrderSet && (
|
||
|
|
<div className="space-y-3">
|
||
|
|
<div className="rounded-lg border border-amber-500/40 bg-amber-500/10 p-4">
|
||
|
|
<p className="text-sm font-medium text-amber-700 dark:text-amber-300">
|
||
|
|
The draft cannot begin until a draft order is set.
|
||
|
|
</p>
|
||
|
|
<p className="mt-1 text-sm text-amber-600 dark:text-amber-400">
|
||
|
|
Randomize the order now, or drag and drop teams into position manually below.
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
{canEditDraftOrder && (
|
||
|
|
<Form method="post">
|
||
|
|
<input type="hidden" name="intent" value="randomize-draft-order" />
|
||
|
|
<Button type="submit" className="w-full" disabled={navigationState === "submitting" && updateIntent === "randomize-draft-order"}>
|
||
|
|
{navigationState === "submitting" && updateIntent === "randomize-draft-order" ? "Randomizing..." : "Randomize Draft Order"}
|
||
|
|
</Button>
|
||
|
|
</Form>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
<div>
|
||
|
|
<h3 className="mb-3 text-sm font-semibold text-muted-foreground">Manually set draft order</h3>
|
||
|
|
<Form method="post" className="space-y-3">
|
||
|
|
<input type="hidden" name="intent" value="set-draft-order" />
|
||
|
|
<DndContext
|
||
|
|
sensors={sensors}
|
||
|
|
collisionDetection={closestCenter}
|
||
|
|
onDragEnd={({ active: activeItem, over }) => {
|
||
|
|
if (!over || activeItem.id === over.id) return;
|
||
|
|
|
||
|
|
setDraftOrderTeams((current) => {
|
||
|
|
const oldIndex = current.indexOf(String(activeItem.id));
|
||
|
|
const newIndex = current.indexOf(String(over.id));
|
||
|
|
if (oldIndex === -1 || newIndex === -1) return current;
|
||
|
|
onDirtyChange(true);
|
||
|
|
return arrayMove(current, oldIndex, newIndex);
|
||
|
|
});
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
<SortableContext items={draftOrderTeams} strategy={verticalListSortingStrategy}>
|
||
|
|
<div className="space-y-3">
|
||
|
|
{draftOrderTeams.map((teamId, index) => {
|
||
|
|
const team = teams.find((t) => t.id === teamId);
|
||
|
|
if (!team) return null;
|
||
|
|
|
||
|
|
return (
|
||
|
|
<SortableDraftOrderRow
|
||
|
|
key={teamId}
|
||
|
|
teamId={teamId}
|
||
|
|
index={index}
|
||
|
|
teamName={team.name}
|
||
|
|
ownerName={team.ownerId ? (ownerMap[team.ownerId] ?? "Unknown") : null}
|
||
|
|
disabled={!canEditDraftOrder}
|
||
|
|
/>
|
||
|
|
);
|
||
|
|
})}
|
||
|
|
</div>
|
||
|
|
</SortableContext>
|
||
|
|
</DndContext>
|
||
|
|
{canEditDraftOrder ? (
|
||
|
|
<div className="rounded-xl border bg-card p-3">
|
||
|
|
{hasUnsavedChanges && (
|
||
|
|
<p className="mb-3 text-sm font-medium text-amber-700 dark:text-amber-300">
|
||
|
|
You have unsaved draft order changes.
|
||
|
|
</p>
|
||
|
|
)}
|
||
|
|
<div className="flex flex-col gap-2 sm:flex-row">
|
||
|
|
{hasUnsavedChanges && (
|
||
|
|
<Button type="button" variant="outline" className="sm:w-48" onClick={onResetChanges}>
|
||
|
|
Reset Changes
|
||
|
|
</Button>
|
||
|
|
)}
|
||
|
|
<Button type="submit" className="flex-1" disabled={navigationState === "submitting" && updateIntent === "set-draft-order"}>
|
||
|
|
{navigationState === "submitting" && updateIntent === "set-draft-order" ? "Saving..." : "Save Draft Order"}
|
||
|
|
</Button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
) : (
|
||
|
|
<p className="text-sm text-muted-foreground">Draft order cannot be changed after the draft has started.</p>
|
||
|
|
)}
|
||
|
|
</Form>
|
||
|
|
</div>
|
||
|
|
{draftOrderSet && canEditDraftOrder && (
|
||
|
|
<Form method="post">
|
||
|
|
<input type="hidden" name="intent" value="randomize-draft-order" />
|
||
|
|
<Button type="submit" variant="outline" className="w-full" disabled={navigationState === "submitting" && updateIntent === "randomize-draft-order"}>
|
||
|
|
{navigationState === "submitting" && updateIntent === "randomize-draft-order" ? "Randomizing..." : "Randomize Draft Order"}
|
||
|
|
</Button>
|
||
|
|
</Form>
|
||
|
|
)}
|
||
|
|
<DraftOrderMessage actionData={actionData} />
|
||
|
|
</SettingsSection>
|
||
|
|
);
|
||
|
|
}
|