feat : update UI

This commit is contained in:
2026-10-01 00:13:38 +07:00 Unverified
parent 2ae4aee962
commit 63d0ec8c67
14 changed files with 6724 additions and 28 deletions
+81 -6
View File
@@ -25,6 +25,7 @@ import {
SortableContext,
sortableKeyboardCoordinates,
useSortable,
verticalListSortingStrategy,
} from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { toast } from "sonner";
@@ -742,6 +743,52 @@ function VisualRecommendationGrid({
);
}
function SortableTeam({
id,
position,
children,
}: {
id: string;
position: number;
children: ReactNode;
}) {
const {
attributes,
listeners,
setNodeRef,
transform,
transition,
isDragging,
} = useSortable({ id });
return (
<div
ref={setNodeRef}
style={{ transform: CSS.Transform.toString(transform), transition }}
className={cn(isDragging && "relative z-10 opacity-60")}
>
<Card size="sm">
<CardContent className="flex flex-col gap-2">
<div className="flex items-center justify-between gap-2">
<span className="text-sm font-medium">Team {position + 1}</span>
<Button
type="button"
variant="ghost"
size="icon-xs"
className="touch-none"
aria-label={`Drag to reorder team ${position + 1}`}
{...attributes}
{...listeners}
>
<GripVerticalIcon />
</Button>
</div>
{children}
</CardContent>
</Card>
</div>
);
}
function BossRecommendationEditor({
scheduleId,
version,
@@ -776,6 +823,13 @@ function BossRecommendationEditor({
.filter((group) => group.kind === "unit")
.flatMap((group) => group.items.flatMap((item) => toInput(item) ?? [])),
);
const teamSensors = useSensors(
useSensor(PointerSensor, { activationConstraint: { distance: 4 } }),
useSensor(KeyboardSensor, {
coordinateGetter: sortableKeyboardCoordinates,
}),
);
const teamIds = teams.map((_, index) => `team-${index}`);
const fingerprint = JSON.stringify({ teams, units });
const valid = teams.every(
(team) =>
@@ -868,9 +922,28 @@ function BossRecommendationEditor({
<div className="flex min-w-0 flex-col gap-6">
<FieldSet className="gap-3">
<FieldLegend>Recommended team</FieldLegend>
{teams.map((team, teamIndex) => (
<Card key={teamIndex} size="sm">
<CardContent className="flex flex-col gap-2">
<DndContext
sensors={teamSensors}
collisionDetection={closestCenter}
onDragEnd={({ active, over }) => {
if (!over || active.id === over.id) return;
const from = teamIds.indexOf(String(active.id));
const to = teamIds.indexOf(String(over.id));
if (from >= 0 && to >= 0)
setTeams((current) => arrayMove(current, from, to));
}}
>
<SortableContext
items={teamIds}
strategy={verticalListSortingStrategy}
>
<div className="flex flex-col gap-3">
{teams.map((team, teamIndex) => (
<SortableTeam
key={teamIds[teamIndex]}
id={teamIds[teamIndex]}
position={teamIndex}
>
<VisualRecommendationGrid
items={team}
limit={4}
@@ -908,9 +981,11 @@ function BossRecommendationEditor({
<Trash2Icon data-icon="inline-start" />
Remove team
</DeleteConfirmationDialog>
</CardContent>
</Card>
))}
</SortableTeam>
))}
</div>
</SortableContext>
</DndContext>
<Button
variant="outline"
size="sm"