97 lines
2.0 KiB
TypeScript
97 lines
2.0 KiB
TypeScript
"use client";
|
|
|
|
import type React from "react";
|
|
import { AlertCircle, Check, GripVertical, Loader2 } from "lucide-react";
|
|
import { useSortable } from "@dnd-kit/sortable";
|
|
import { CSS } from "@dnd-kit/utilities";
|
|
import { Button } from "@/components/ui/button";
|
|
import type { AutoSaveStatus } from "@/hooks/use-debounced-autosave";
|
|
|
|
export function SaveStatusIndicator({
|
|
status,
|
|
error,
|
|
onRetry,
|
|
}: {
|
|
status: AutoSaveStatus;
|
|
error: string | null;
|
|
onRetry: () => Promise<void>;
|
|
}) {
|
|
if (status === "clean") return null;
|
|
|
|
return (
|
|
<span className="flex items-center gap-1.5 text-xs text-muted-foreground" aria-live="polite">
|
|
{status === "dirty" && "Unsaved"}
|
|
{status === "saving" && (
|
|
<>
|
|
<Loader2 className="animate-spin" />
|
|
Saving
|
|
</>
|
|
)}
|
|
{status === "saved" && (
|
|
<>
|
|
<Check />
|
|
Saved
|
|
</>
|
|
)}
|
|
{status === "error" && (
|
|
<>
|
|
<AlertCircle />
|
|
<span>{error || "Could not save."}</span>
|
|
<Button
|
|
variant="link"
|
|
size="sm"
|
|
className="h-auto px-1"
|
|
onClick={() => void onRetry().catch(() => undefined)}
|
|
>
|
|
Retry
|
|
</Button>
|
|
</>
|
|
)}
|
|
</span>
|
|
);
|
|
}
|
|
|
|
export function SortableQuestion({
|
|
id,
|
|
position,
|
|
children,
|
|
}: {
|
|
id: string;
|
|
position: number;
|
|
children: React.ReactNode;
|
|
}) {
|
|
const {
|
|
attributes,
|
|
listeners,
|
|
setNodeRef,
|
|
transform,
|
|
transition,
|
|
isDragging,
|
|
} = useSortable({ id });
|
|
|
|
return (
|
|
<div
|
|
ref={setNodeRef}
|
|
className="relative"
|
|
style={{
|
|
transform: CSS.Transform.toString(transform),
|
|
transition,
|
|
opacity: isDragging ? 0.6 : 1,
|
|
}}
|
|
>
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
size="icon"
|
|
className="absolute left-2 top-4"
|
|
aria-label={`Drag question ${position} to reorder`}
|
|
{...attributes}
|
|
{...listeners}
|
|
>
|
|
<GripVertical />
|
|
</Button>
|
|
{children}
|
|
</div>
|
|
);
|
|
}
|