feat(frontend): library page, components, and schedule/add-to-block dialogs (tasks 11-14)
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useMemo } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { useChannels, useChannel, useUpdateChannel } from "@/hooks/use-channels";
|
||||
import type { LibraryItemFull, ScheduleConfig } from "@/lib/types";
|
||||
import { WEEKDAYS } from "@/lib/types";
|
||||
|
||||
interface Props {
|
||||
selectedItems: LibraryItemFull[];
|
||||
}
|
||||
|
||||
export function AddToBlockDialog({ selectedItems }: Props) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [channelId, setChannelId] = useState("");
|
||||
const [blockId, setBlockId] = useState("");
|
||||
|
||||
const { data: channels } = useChannels();
|
||||
const { data: channel } = useChannel(channelId);
|
||||
const updateChannel = useUpdateChannel();
|
||||
|
||||
const manualBlocks = useMemo(() => {
|
||||
if (!channel) return [];
|
||||
const seen = new Set<string>();
|
||||
const result: { id: string; name: string }[] = [];
|
||||
for (const day of WEEKDAYS) {
|
||||
for (const block of channel.schedule_config.day_blocks[day] ?? []) {
|
||||
if (block.content.type === "manual" && !seen.has(block.id)) {
|
||||
seen.add(block.id);
|
||||
result.push({ id: block.id, name: block.name });
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}, [channel]);
|
||||
|
||||
async function handleConfirm() {
|
||||
if (!channel || !blockId) return;
|
||||
const updatedDayBlocks = { ...channel.schedule_config.day_blocks };
|
||||
for (const day of WEEKDAYS) {
|
||||
updatedDayBlocks[day] = (updatedDayBlocks[day] ?? []).map(block => {
|
||||
if (block.id !== blockId || block.content.type !== "manual") return block;
|
||||
return {
|
||||
...block,
|
||||
content: {
|
||||
...block.content,
|
||||
items: [...block.content.items, ...selectedItems.map(i => i.id)],
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
const scheduleConfig: ScheduleConfig = { day_blocks: updatedDayBlocks };
|
||||
|
||||
await updateChannel.mutateAsync({
|
||||
id: channelId,
|
||||
data: { schedule_config: scheduleConfig },
|
||||
});
|
||||
setOpen(false);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button size="sm" variant="outline" onClick={() => setOpen(true)}>Add to block</Button>
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogContent className="max-w-sm">
|
||||
<DialogHeader><DialogTitle>Add to existing block</DialogTitle></DialogHeader>
|
||||
<div className="flex flex-col gap-4">
|
||||
<div>
|
||||
<p className="mb-1.5 text-xs text-zinc-400">Channel</p>
|
||||
<Select value={channelId} onValueChange={v => { setChannelId(v); setBlockId(""); }}>
|
||||
<SelectTrigger><SelectValue placeholder="Select channel…" /></SelectTrigger>
|
||||
<SelectContent>
|
||||
{channels?.map(c => (
|
||||
<SelectItem key={c.id} value={c.id}>{c.name}</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
{channelId && (
|
||||
<div>
|
||||
<p className="mb-1.5 text-xs text-zinc-400">Manual block</p>
|
||||
{manualBlocks.length === 0 ? (
|
||||
<p className="text-xs text-zinc-500">No manual blocks in this channel.</p>
|
||||
) : (
|
||||
<Select value={blockId} onValueChange={setBlockId}>
|
||||
<SelectTrigger><SelectValue placeholder="Select block…" /></SelectTrigger>
|
||||
<SelectContent>
|
||||
{manualBlocks.map(b => (
|
||||
<SelectItem key={b.id} value={b.id}>{b.name}</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<p className="text-xs text-zinc-500">Adding {selectedItems.length} item(s) to selected block.</p>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => setOpen(false)}>Cancel</Button>
|
||||
<Button disabled={!blockId || updateChannel.isPending} onClick={handleConfirm}>
|
||||
{updateChannel.isPending ? "Saving…" : "Add items"}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user