A social knowledge tool for researchers built on ATProto
at development 4.8 kB view raw
1'use client'; 2 3import { useState, useEffect, useMemo, useCallback } from 'react'; 4import { createClientTokenManager } from '@/services/auth'; 5import { ApiClient } from '@/api-client/ApiClient'; 6import { Button, Group, Modal, Stack, Text } from '@mantine/core'; 7import { CollectionSelector } from './CollectionSelector'; 8 9interface Collection { 10 id: string; 11 name: string; 12 description?: string; 13 cardCount: number; 14 authorId: string; 15} 16 17interface AddToCollectionModalProps { 18 cardId: string; 19 isOpen: boolean; 20 onClose: () => void; 21 onSuccess?: () => void; 22} 23 24export function AddToCollectionModal({ 25 cardId, 26 isOpen, 27 onClose, 28 onSuccess, 29}: AddToCollectionModalProps) { 30 const [selectedCollectionIds, setSelectedCollectionIds] = useState<string[]>( 31 [], 32 ); 33 const [submitting, setSubmitting] = useState(false); 34 const [error, setError] = useState(''); 35 const [card, setCard] = useState<any>(null); 36 const [loading, setLoading] = useState(false); 37 38 // Create API client instance 39 const apiClient = new ApiClient( 40 process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:3000', 41 createClientTokenManager(), 42 ); 43 44 // Get existing collections for this card 45 const existingCollections = useMemo(() => { 46 if (!card) return []; 47 return card.collections || []; 48 }, [card]); 49 50 const fetchCard = useCallback(async () => { 51 // Create API client instance 52 const apiClient = new ApiClient( 53 process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:3000', 54 createClientTokenManager(), 55 ); 56 57 try { 58 setLoading(true); 59 setError(''); 60 const response = await apiClient.getUrlCardView(cardId); 61 setCard(response); 62 } catch (error: any) { 63 console.error('Error fetching card:', error); 64 setError(error.message || 'Failed to load card details'); 65 } finally { 66 setLoading(false); 67 } 68 }, [cardId]); 69 70 useEffect(() => { 71 if (isOpen) { 72 fetchCard(); 73 } 74 }, [isOpen, cardId, fetchCard]); 75 76 const handleSubmit = async () => { 77 if (selectedCollectionIds.length === 0) { 78 setError('Please select at least one collection'); 79 return; 80 } 81 82 setSubmitting(true); 83 setError(''); 84 85 try { 86 // Create API client instance 87 const apiClient = new ApiClient( 88 process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:3000', 89 createClientTokenManager(), 90 ); 91 92 // Add card to all selected collections in a single request 93 await apiClient.addCardToCollection({ 94 cardId, 95 collectionIds: selectedCollectionIds, 96 }); 97 98 // Success 99 onSuccess?.(); 100 onClose(); 101 setSelectedCollectionIds([]); 102 } catch (error: any) { 103 console.error('Error adding card to collections:', error); 104 setError(error.message || 'Failed to add card to collections'); 105 } finally { 106 setSubmitting(false); 107 } 108 }; 109 110 const handleClose = () => { 111 if (!submitting) { 112 onClose(); 113 setSelectedCollectionIds([]); 114 setError(''); 115 } 116 }; 117 118 if (!isOpen) return null; 119 120 return ( 121 <Modal 122 opened={isOpen} 123 onClose={handleClose} 124 title="Add to Collections" 125 centered 126 size="md" 127 > 128 <Stack p="sm"> 129 {loading ? ( 130 <Text size="sm" c="dimmed" ta="center" py="md"> 131 Loading card details... 132 </Text> 133 ) : error ? ( 134 <Stack align="center"> 135 <Text c="red">{error}</Text> 136 <Button onClick={fetchCard} variant="outline" size="sm"> 137 Try Again 138 </Button> 139 </Stack> 140 ) : ( 141 <> 142 <CollectionSelector 143 apiClient={apiClient} 144 selectedCollectionIds={selectedCollectionIds} 145 onSelectionChange={setSelectedCollectionIds} 146 existingCollections={existingCollections} 147 disabled={submitting} 148 showCreateOption={true} 149 placeholder="Search collections to add..." 150 /> 151 152 {error && ( 153 <Text c="red" size="sm"> 154 {error} 155 </Text> 156 )} 157 158 <Group gap={'xs'} grow> 159 <Button 160 onClick={handleSubmit} 161 disabled={submitting || selectedCollectionIds.length === 0} 162 loading={submitting} 163 > 164 {submitting 165 ? 'Adding...' 166 : `Add to ${selectedCollectionIds.length} Collection${selectedCollectionIds.length !== 1 && 's'}`} 167 </Button> 168 <Button 169 variant="outline" 170 onClick={handleClose} 171 disabled={submitting} 172 > 173 Cancel 174 </Button> 175 </Group> 176 </> 177 )} 178 </Stack> 179 </Modal> 180 ); 181}