Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 23 additions & 82 deletions src/pages/admin/festivals/LinkWizard/LinkWizard.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
import { useState } from "react";
import { Loader2, LinkIcon } from "lucide-react";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import {
useArtistsMissingLinksByEditionQuery,
useUntypedSetsByEditionQuery,
} from "@/api/artists/useArtistsMissingLinksByEdition";
import { usePrefetchNextBatchLinks } from "@/api/artistSearch/usePrefetchNextBatchLinks";
import { useIsMobile } from "@/hooks/use-mobile";
import { filterArtistsByStage } from "@/lib/filterArtistsByStage";
import { useLinkWizardSkipped } from "@/hooks/useLinkWizardSkipped";
import {
buildLinkWizardQueue,
type LinkWizardQueueItem,
} from "./buildLinkWizardQueue";
import { useLinkWizardQueue } from "./useLinkWizardQueue";
import type { LinkWizardQueueItem } from "./buildLinkWizardQueue";
import { LinkWizardQueue } from "./LinkWizardQueue";
import { LinkWizardStep } from "./LinkWizardStep";
import { SetTypeBackfillStep } from "./SetTypeBackfillStep";
Expand All @@ -22,38 +17,14 @@ interface LinkWizardProps {
}

export function LinkWizard({ editionId }: LinkWizardProps) {
const isMobile = useIsMobile();
const artistsQuery = useArtistsMissingLinksByEditionQuery(editionId);
const untypedSetsQuery = useUntypedSetsByEditionQuery(editionId);
const [currentItemId, setCurrentItemId] = useState<string | undefined>(
undefined,
);
const [selectedStages, setSelectedStages] = useState<string[]>([]);
const [showFullQueue, setShowFullQueue] = useState(false);
const skippedHook = useLinkWizardSkipped(editionId);

const allArtists = artistsQuery.data ?? [];
const unskippedArtists = allArtists.filter(
(artist) => !skippedHook.isSkipped(artist.id),
);
const filteredArtists = filterArtistsByStage(
unskippedArtists,
selectedStages,
);

const queueItems = buildLinkWizardQueue(
filteredArtists,
untypedSetsQuery.data ?? [],
selectedStages,
);

const currentIndex = currentItemId
? Math.max(
0,
queueItems.findIndex((item) => item.id === currentItemId),
)
: 0;
const currentItem = queueItems[Math.min(currentIndex, queueItems.length - 1)];
const untypedSets = untypedSetsQuery.data ?? [];
const queue = useLinkWizardQueue(allArtists, untypedSets, skippedHook);
const { items, artists: filteredArtists, currentItem, position, total } =
queue;

usePrefetchNextBatchLinks(
filteredArtists,
Expand All @@ -75,14 +46,12 @@ export function LinkWizard({ editionId }: LinkWizardProps) {
<div className="space-y-6 lg:grid lg:grid-cols-[280px_minmax(0,1fr)] lg:gap-6 lg:items-start">
<div className="lg:sticky lg:top-4">
<LinkWizardQueue
items={queueItems}
items={items}
currentItemId={currentItem?.id}
onSelectItem={handleSelectItem}
selectedStages={selectedStages}
onStageToggle={handleStageToggle}
onClearStages={() => setSelectedStages([])}
isPreviewMode={isMobile && !showFullQueue}
onViewAll={() => setShowFullQueue(true)}
onSelectItem={queue.selectItem}
selectedStages={queue.selectedStages}
onStageToggle={queue.toggleStage}
onClearStages={queue.clearStages}
skippedArtists={skippedHook.getSkippedArtists()}
allArtists={allArtists}
onRestoreSkipped={skippedHook.restore}
Expand All @@ -97,68 +66,40 @@ export function LinkWizard({ editionId }: LinkWizardProps) {
</CardTitle>
</CardHeader>
<CardContent>
{queueItems.length === 0 ? (
{items.length === 0 ? (
<p className="text-muted-foreground">
{selectedStages.length > 0
{queue.selectedStages.length > 0
? "No artists missing links and no sets missing a type on selected stages."
: "All artists in this edition have both links set and all sets have a type."}
</p>
) : currentItem?.kind === "artist" ? (
<LinkWizardStep
key={currentItem.id}
artist={currentItem.artist}
position={currentIndex + 1}
total={queueItems.length}
position={position}
total={total}
artists={filteredArtists}
onPrev={() => goTo(currentIndex - 1)}
onNext={() => {
skippedHook.markSkipped(currentItem.id);
goTo(nextIndexAfterRemoval());
}}
onSaveSuccess={() => {
skippedHook.markSaved(currentItem.id);
goTo(nextIndexAfterRemoval());
}}
onPrev={queue.prev}
onNext={queue.skip}
onSaveSuccess={queue.save}
/>
) : (
currentItem && (
<SetTypeBackfillStep
key={currentItem.id}
set={currentItem.set}
position={currentIndex + 1}
total={queueItems.length}
onPrev={() => goTo(currentIndex - 1)}
onNext={() => goTo(currentIndex + 1)}
onSaveSuccess={() => goTo(nextIndexAfterRemoval())}
position={position}
total={total}
onPrev={queue.prev}
onNext={queue.skip}
onSaveSuccess={queue.save}
/>
)
)}
</CardContent>
</Card>
</div>
);

function goTo(index: number) {
const clamped = Math.max(0, Math.min(index, queueItems.length - 1));
setCurrentItemId(queueItems[clamped]?.id);
}

function nextIndexAfterRemoval() {
const isLast = currentIndex >= queueItems.length - 1;
return isLast ? currentIndex - 1 : currentIndex + 1;
}

function handleSelectItem(item: LinkWizardQueueItem) {
setCurrentItemId(item.id);
}

function handleStageToggle(stageId: string) {
setSelectedStages((prev) =>
prev.includes(stageId)
? prev.filter((id) => id !== stageId)
: [...prev, stageId],
);
}
}

function itemName(item: LinkWizardQueueItem) {
Expand Down
11 changes: 6 additions & 5 deletions src/pages/admin/festivals/LinkWizard/LinkWizardQueue.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { useState } from "react";
import { Tag } from "lucide-react";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Button } from "@/components/ui/button";
import type { ArtistWithSets } from "@/api/artists/useArtistsMissingLinksByEdition";
import type { ArtistSkipRecord } from "@/hooks/useLinkWizardSkipped";
import { useIsMobile } from "@/hooks/use-mobile";
import { cn } from "@/lib/utils";
import type { LinkWizardQueueItem } from "./buildLinkWizardQueue";
import { LinkWizardStageFilterDropdown } from "./LinkWizardStageFilterDropdown";
Expand All @@ -19,8 +21,6 @@ interface LinkWizardQueueProps {
selectedStages: string[];
onStageToggle: (stageId: string) => void;
onClearStages: () => void;
isPreviewMode?: boolean;
onViewAll?: () => void;
skippedArtists: ArtistSkipRecord[];
allArtists: Array<{ id: string; name: string }>;
onRestoreSkipped: (artistId: string) => void;
Expand All @@ -34,13 +34,14 @@ export function LinkWizardQueue({
selectedStages,
onStageToggle,
onClearStages,
isPreviewMode = false,
onViewAll,
skippedArtists,
allArtists,
onRestoreSkipped,
onClearAllSkipped,
}: LinkWizardQueueProps) {
const isMobile = useIsMobile();
const [showFullQueue, setShowFullQueue] = useState(false);
const isPreviewMode = isMobile && !showFullQueue;
const displayedItems = isPreviewMode
? items.slice(0, MOBILE_PREVIEW_COUNT)
: items;
Expand Down Expand Up @@ -104,7 +105,7 @@ export function LinkWizardQueue({
variant="ghost"
size="sm"
className="w-full text-xs text-accent hover:text-accent hover:bg-accent-soft"
onClick={onViewAll}
onClick={() => setShowFullQueue(true)}
>
View all ({items.length - MOBILE_PREVIEW_COUNT} more)
</Button>
Expand Down
Loading
Loading