Initial commit
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
"use client";
|
||||
import { useState, useTransition } from "react";
|
||||
import Link from "next/link";
|
||||
import { FolderHeart, X, Plus } from "lucide-react";
|
||||
import { addImageToCollection, removeImageFromCollection, createCollection } from "@/app/actions/collections";
|
||||
|
||||
interface PickedCollection { id: number; name: string; slug?: string }
|
||||
|
||||
export function CollectionPicker({
|
||||
imageId,
|
||||
current,
|
||||
available,
|
||||
}: {
|
||||
imageId: number;
|
||||
current: PickedCollection[];
|
||||
available: PickedCollection[];
|
||||
}) {
|
||||
const [picked, setPicked] = useState(current);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [draft, setDraft] = useState("");
|
||||
const [pool, setPool] = useState(available);
|
||||
const [, start] = useTransition();
|
||||
|
||||
const add = (c: PickedCollection) => {
|
||||
if (picked.some(p => p.id === c.id)) return;
|
||||
setPicked((cur) => [...cur, c]);
|
||||
start(async () => { await addImageToCollection(c.id, imageId); });
|
||||
};
|
||||
|
||||
const remove = (id: number) => {
|
||||
setPicked((cur) => cur.filter(c => c.id !== id));
|
||||
start(async () => { await removeImageFromCollection(id, imageId); });
|
||||
};
|
||||
|
||||
const createNew = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
const name = draft.trim();
|
||||
if (!name) return;
|
||||
setDraft("");
|
||||
start(async () => {
|
||||
const created = await createCollection(name);
|
||||
if (created) {
|
||||
setPool((cur) => [...cur, { id: created.id, name, slug: created.slug }]);
|
||||
setPicked((cur) => [...cur, { id: created.id, name, slug: created.slug }]);
|
||||
await addImageToCollection(created.id, imageId);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const remaining = pool.filter(p => !picked.some(pp => pp.id === p.id));
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="text-[10px] uppercase tracking-wider font-mono text-[var(--color-fg-muted)] mb-2">
|
||||
Collections
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{picked.map((c) => (
|
||||
<span
|
||||
key={c.id}
|
||||
className="group flex items-center gap-1 px-2 py-1 rounded-full text-xs glass border-[var(--color-cyan)]/30 text-[var(--color-cyan)] bg-[color-mix(in_oklch,var(--color-cyan)_10%,transparent)]"
|
||||
>
|
||||
<FolderHeart className="w-3 h-3" />
|
||||
{c.slug ? (
|
||||
<Link
|
||||
href={`/collection/${c.slug}`}
|
||||
className="hover:underline"
|
||||
>
|
||||
{c.name}
|
||||
</Link>
|
||||
) : (
|
||||
c.name
|
||||
)}
|
||||
<button onClick={() => remove(c.id)} className="opacity-50 hover:opacity-100">
|
||||
<X className="w-3 h-3" />
|
||||
</button>
|
||||
</span>
|
||||
))}
|
||||
<button
|
||||
onClick={() => setOpen((o) => !o)}
|
||||
className="flex items-center gap-1 px-2 py-1 rounded-full text-xs border border-dashed border-[var(--color-glass-border)] text-[var(--color-fg-dim)] hover:text-[var(--color-cyan)] hover:border-[var(--color-cyan)]"
|
||||
>
|
||||
<Plus className="w-3 h-3" />
|
||||
Add to collection
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{open && (
|
||||
<div className="mt-2 glass rounded-xl p-3 space-y-2">
|
||||
{remaining.length > 0 && (
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{remaining.map((c) => (
|
||||
<button
|
||||
key={c.id}
|
||||
onClick={() => add(c)}
|
||||
className="text-xs px-2 py-1 rounded-full glass-strong hover:text-[var(--color-cyan)]"
|
||||
>
|
||||
{c.name}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<form onSubmit={createNew} className="flex gap-1.5">
|
||||
<input
|
||||
value={draft}
|
||||
onChange={(e) => setDraft(e.target.value)}
|
||||
placeholder="New collection…"
|
||||
className="flex-1 bg-transparent text-xs px-2 py-1 rounded-md border border-[var(--color-glass-border)] outline-none focus:border-[var(--color-cyan)]"
|
||||
/>
|
||||
<button type="submit" className="text-xs px-2 py-1 rounded-md bg-[var(--color-cyan)] text-black font-medium">
|
||||
Create
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user