'use client'; import { useState, useEffect } from 'react'; import { searchApi, tagApi } from '../../lib/api'; import { Story, Tag } from '../../types/api'; import { Input } from '../ui/Input'; import Button from '../ui/Button'; import LoadingSpinner from '../ui/LoadingSpinner'; interface CollectionFormProps { initialData?: { name: string; description?: string; tags?: string[]; storyIds?: string[]; coverImagePath?: string; }; onSubmit: (data: { name: string; description?: string; tags?: string[]; storyIds?: string[]; coverImage?: File; }) => Promise; onCancel: () => void; loading?: boolean; submitLabel?: string; } export default function CollectionForm({ initialData, onSubmit, onCancel, loading = false, submitLabel = 'Save Collection' }: CollectionFormProps) { const [name, setName] = useState(initialData?.name || ''); const [description, setDescription] = useState(initialData?.description || ''); const [tagInput, setTagInput] = useState(''); const [selectedTags, setSelectedTags] = useState(initialData?.tags || []); const [tagSuggestions, setTagSuggestions] = useState([]); const [selectedStoryIds, setSelectedStoryIds] = useState(initialData?.storyIds || []); const [coverImage, setCoverImage] = useState(null); const [coverImagePreview, setCoverImagePreview] = useState(null); // Story selection state const [storySearchQuery, setStorySearchQuery] = useState(''); const [availableStories, setAvailableStories] = useState([]); const [selectedStories, setSelectedStories] = useState([]); const [loadingStories, setLoadingStories] = useState(false); const [showStorySelection, setShowStorySelection] = useState(false); // Load tag suggestions when typing useEffect(() => { if (tagInput.length > 1) { const loadSuggestions = async () => { try { const suggestions = await tagApi.getTagAutocomplete(tagInput); setTagSuggestions(suggestions.filter(tag => !selectedTags.includes(tag))); } catch (error) { console.error('Failed to load tag suggestions:', error); } }; const debounceTimer = setTimeout(loadSuggestions, 300); return () => clearTimeout(debounceTimer); } else { setTagSuggestions([]); } }, [tagInput, selectedTags]); // Load stories for selection useEffect(() => { if (showStorySelection) { const loadStories = async () => { try { setLoadingStories(true); const result = await searchApi.search({ query: storySearchQuery || '*', page: 0, size: 50, }); setAvailableStories(result.results || []); } catch (error) { console.error('Failed to load stories:', error); } finally { setLoadingStories(false); } }; const debounceTimer = setTimeout(loadStories, 300); return () => clearTimeout(debounceTimer); } }, [storySearchQuery, showStorySelection]); // Load selected stories data on mount useEffect(() => { if (selectedStoryIds.length > 0) { const loadSelectedStories = async () => { try { const result = await searchApi.search({ query: '*', page: 0, size: 100, }); const stories = result.results.filter(story => selectedStoryIds.includes(story.id)); setSelectedStories(stories); } catch (error) { console.error('Failed to load selected stories:', error); } }; loadSelectedStories(); } }, [selectedStoryIds]); const handleTagInputKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter' && tagInput.trim()) { e.preventDefault(); const newTag = tagInput.trim(); if (!selectedTags.includes(newTag)) { setSelectedTags(prev => [...prev, newTag]); } setTagInput(''); setTagSuggestions([]); } }; const addTag = (tag: string) => { if (!selectedTags.includes(tag)) { setSelectedTags(prev => [...prev, tag]); } setTagInput(''); setTagSuggestions([]); }; const removeTag = (tagToRemove: string) => { setSelectedTags(prev => prev.filter(tag => tag !== tagToRemove)); }; const handleCoverImageChange = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (file) { setCoverImage(file); const reader = new FileReader(); reader.onload = (e) => { setCoverImagePreview(e.target?.result as string); }; reader.readAsDataURL(file); } }; const toggleStorySelection = (story: Story) => { const isSelected = selectedStoryIds.includes(story.id); if (isSelected) { setSelectedStoryIds(prev => prev.filter(id => id !== story.id)); setSelectedStories(prev => prev.filter(s => s.id !== story.id)); } else { setSelectedStoryIds(prev => [...prev, story.id]); setSelectedStories(prev => [...prev, story]); } }; const removeSelectedStory = (storyId: string) => { setSelectedStoryIds(prev => prev.filter(id => id !== storyId)); setSelectedStories(prev => prev.filter(s => s.id !== storyId)); }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!name.trim()) { return; } await onSubmit({ name: name.trim(), description: description.trim() || undefined, tags: selectedTags, storyIds: selectedStoryIds, coverImage: coverImage || undefined, }); }; return (
{/* Basic Information */}

Basic Information

setName(e.target.value)} placeholder="Enter collection name" required className="w-full" />