A social knowledge tool for researchers built on ATProto
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}