|
1 | 1 | "use client"; |
| 2 | +import MyContributionsPage from "@/components/contributions"; |
| 3 | +import React from "react"; |
2 | 4 |
|
3 | | -import { ActiveContributions } from "@/components/contributions/active-contribution"; |
4 | | -import { CallToAction } from "@/components/contributions/call-to-action"; |
5 | | -import { CommentEditModal } from "@/components/contributions/comment-edit-modal"; |
6 | | -import { ContributionFilters } from "@/components/contributions/contribution-filters"; |
7 | | -import { ContributionStats as ContributionStatsComponent } from "@/components/contributions/contribution-stats"; |
8 | | -import { DeleteConfirmationDialog } from "@/components/contributions/delete-confirmation-dialog"; |
9 | | -import { LoadingState } from "@/components/contributions/loading-state"; |
10 | | -import { PastContributions } from "@/components/contributions/past-contributions"; |
11 | | -import { UserComments } from "@/components/contributions/user-comments"; |
12 | | -import { |
13 | | - deleteComment as apiDeleteComment, |
14 | | - editComment as apiEditComment, |
15 | | - fetchActiveProjects, |
16 | | - fetchCategories, |
17 | | - fetchContributionStats, |
18 | | - fetchPastProjects, |
19 | | - fetchUserComments, |
20 | | -} from "@/lib/actions/services"; |
21 | | -import { |
22 | | - sortActiveProjects, |
23 | | - sortComments, |
24 | | - sortPastProjects, |
25 | | -} from "@/lib/utils"; |
26 | | -import type { |
27 | | - ActiveProject, |
28 | | - ContributionStats, |
29 | | - PastProject, |
30 | | - SortOption, |
31 | | - TabOption, |
32 | | - UserComment, |
33 | | -} from "@/types/contributions"; |
34 | | -import { useRouter } from "next/navigation"; |
35 | | -import { useEffect, useState } from "react"; |
36 | | -import { toast } from "sonner"; |
| 5 | +const Page = () => { |
| 6 | + return <MyContributionsPage />; |
| 7 | +}; |
37 | 8 |
|
38 | | -export default function MyContributionsPage() { |
39 | | - const router = useRouter(); |
40 | | - const [activeTab, setActiveTab] = useState<TabOption>("all"); |
41 | | - const [searchQuery, setSearchQuery] = useState(""); |
42 | | - const [sortOption, setSortOption] = useState<SortOption>("newest"); |
43 | | - const [categoryFilter, setCategoryFilter] = useState("all"); |
44 | | - const [categories, setCategories] = useState<string[]>([]); |
45 | | - |
46 | | - // Data states - Fix type definitions |
47 | | - const [stats, setStats] = useState<ContributionStats | null>(null); |
48 | | - const [activeProjects, setActiveProjects] = useState<ActiveProject[]>([]); |
49 | | - const [pastProjects, setPastProjects] = useState<PastProject[]>([]); |
50 | | - const [comments, setComments] = useState<UserComment[]>([]); |
51 | | - |
52 | | - // Loading states |
53 | | - const [isLoadingStats, setIsLoadingStats] = useState(true); |
54 | | - const [isLoadingActive, setIsLoadingActive] = useState(true); |
55 | | - const [isLoadingPast, setIsLoadingPast] = useState(true); |
56 | | - const [isLoadingComments, setIsLoadingComments] = useState(true); |
57 | | - |
58 | | - // Modal states |
59 | | - const [commentToEdit, setCommentToEdit] = useState<UserComment | null>(null); |
60 | | - const [commentToDelete, setCommentToDelete] = useState<string | null>(null); |
61 | | - |
62 | | - // Fetch data on initial load |
63 | | - useEffect(() => { |
64 | | - const fetchData = async () => { |
65 | | - try { |
66 | | - // Fetch categories |
67 | | - const categoriesData = await fetchCategories(); |
68 | | - setCategories(categoriesData); |
69 | | - |
70 | | - // Fetch stats |
71 | | - setIsLoadingStats(true); |
72 | | - const statsData = await fetchContributionStats(); |
73 | | - setStats(statsData); |
74 | | - setIsLoadingStats(false); |
75 | | - |
76 | | - // Fetch active projects |
77 | | - setIsLoadingActive(true); |
78 | | - const activeData = await fetchActiveProjects(); |
79 | | - setActiveProjects(activeData); |
80 | | - setIsLoadingActive(false); |
81 | | - |
82 | | - // Fetch past projects |
83 | | - setIsLoadingPast(true); |
84 | | - const pastData = await fetchPastProjects(); |
85 | | - setPastProjects(pastData); |
86 | | - setIsLoadingPast(false); |
87 | | - |
88 | | - // Fetch comments |
89 | | - setIsLoadingComments(true); |
90 | | - const commentsData = await fetchUserComments(); |
91 | | - setComments(commentsData); |
92 | | - setIsLoadingComments(false); |
93 | | - } catch (error) { |
94 | | - console.error("Error fetching data:", error); |
95 | | - toast.error("Error", { |
96 | | - description: "Failed to load your contributions. Please try again.", |
97 | | - }); |
98 | | - } |
99 | | - }; |
100 | | - |
101 | | - fetchData(); |
102 | | - }, []); |
103 | | - |
104 | | - // Fetch data when category filter changes |
105 | | - useEffect(() => { |
106 | | - const fetchFilteredData = async () => { |
107 | | - try { |
108 | | - if (activeTab === "all" || activeTab === "votes") { |
109 | | - // Fetch active projects with category filter |
110 | | - setIsLoadingActive(true); |
111 | | - const activeData = await fetchActiveProjects(categoryFilter); |
112 | | - setActiveProjects(activeData); |
113 | | - setIsLoadingActive(false); |
114 | | - |
115 | | - // Fetch past projects with category filter |
116 | | - setIsLoadingPast(true); |
117 | | - const pastData = await fetchPastProjects(categoryFilter); |
118 | | - setPastProjects(pastData); |
119 | | - setIsLoadingPast(false); |
120 | | - } |
121 | | - } catch (error) { |
122 | | - console.error("Error fetching filtered data:", error); |
123 | | - toast.error("Error", { |
124 | | - description: "Failed to load filtered data. Please try again.", |
125 | | - }); |
126 | | - } |
127 | | - }; |
128 | | - |
129 | | - fetchFilteredData(); |
130 | | - }, [categoryFilter, activeTab]); |
131 | | - |
132 | | - // Fetch comments when search query changes |
133 | | - useEffect(() => { |
134 | | - const fetchFilteredComments = async () => { |
135 | | - if (activeTab === "all" || activeTab === "comments") { |
136 | | - try { |
137 | | - setIsLoadingComments(true); |
138 | | - const commentsData = await fetchUserComments(searchQuery); |
139 | | - setComments(commentsData); |
140 | | - setIsLoadingComments(false); |
141 | | - } catch (error) { |
142 | | - console.error("Error fetching comments:", error); |
143 | | - toast.error("Error", { |
144 | | - description: "Failed to load comments. Please try again.", |
145 | | - }); |
146 | | - } |
147 | | - } |
148 | | - }; |
149 | | - |
150 | | - // Debounce search to avoid too many requests |
151 | | - const debounceTimeout = setTimeout(() => { |
152 | | - fetchFilteredComments(); |
153 | | - }, 500); |
154 | | - |
155 | | - return () => clearTimeout(debounceTimeout); |
156 | | - }, [searchQuery, activeTab]); |
157 | | - |
158 | | - // Sort data based on selected option |
159 | | - const sortedActiveProjects = activeProjects |
160 | | - ? sortActiveProjects(activeProjects, sortOption) |
161 | | - : []; |
162 | | - const sortedPastProjects = pastProjects |
163 | | - ? sortPastProjects(pastProjects, sortOption) |
164 | | - : []; |
165 | | - const sortedComments = comments ? sortComments(comments, sortOption) : []; |
166 | | - |
167 | | - // Navigation and action handlers |
168 | | - const navigateToProject = (projectId: string) => { |
169 | | - router.push(`/projects/${projectId}`); |
170 | | - }; |
171 | | - |
172 | | - // Fix the type to match what UserComments component expects |
173 | | - const handleEditComment = (commentId: string) => { |
174 | | - const comment = comments.find((c) => c.id === commentId); |
175 | | - if (comment) { |
176 | | - setCommentToEdit(comment); |
177 | | - } |
178 | | - }; |
179 | | - |
180 | | - const handleDeleteComment = (commentId: string) => { |
181 | | - setCommentToDelete(commentId); |
182 | | - }; |
183 | | - |
184 | | - const saveEditedComment = async (id: string, content: string) => { |
185 | | - try { |
186 | | - await apiEditComment(id, content); |
187 | | - |
188 | | - // Update local state |
189 | | - setComments((prevComments) => |
190 | | - prevComments.map((comment) => |
191 | | - comment.id === id ? { ...comment, content } : comment, |
192 | | - ), |
193 | | - ); |
194 | | - |
195 | | - toast.success("Success", { |
196 | | - description: "Comment updated successfully", |
197 | | - }); |
198 | | - } catch (error) { |
199 | | - console.error("Error updating comment:", error); |
200 | | - toast.error("Error", { |
201 | | - description: "Failed to update comment. Please try again.", |
202 | | - }); |
203 | | - throw error; // Re-throw to handle in the modal |
204 | | - } |
205 | | - }; |
206 | | - |
207 | | - const confirmDeleteComment = async () => { |
208 | | - if (!commentToDelete) return; |
209 | | - |
210 | | - try { |
211 | | - await apiDeleteComment(commentToDelete); |
212 | | - |
213 | | - // Update local state |
214 | | - setComments((prevComments) => |
215 | | - prevComments.filter((comment) => comment.id !== commentToDelete), |
216 | | - ); |
217 | | - |
218 | | - toast.success("Success", { |
219 | | - description: "Comment deleted successfully", |
220 | | - }); |
221 | | - } catch (error) { |
222 | | - console.error("Error deleting comment:", error); |
223 | | - toast.error("Error", { |
224 | | - description: "Failed to delete comment. Please try again.", |
225 | | - }); |
226 | | - throw error; // Re-throw to handle in the dialog |
227 | | - } |
228 | | - }; |
229 | | - |
230 | | - return ( |
231 | | - <div className="container mx-auto py-8 max-w-7xl"> |
232 | | - <h1 className="text-3xl font-bold mb-6">My Contributions</h1> |
233 | | - |
234 | | - {/* Summary Section */} |
235 | | - {isLoadingStats ? ( |
236 | | - <LoadingState type="stats" /> |
237 | | - ) : stats ? ( |
238 | | - <ContributionStatsComponent stats={stats} /> |
239 | | - ) : null} |
240 | | - |
241 | | - {/* Tabs and Filters */} |
242 | | - <ContributionFilters |
243 | | - activeTab={activeTab} |
244 | | - setActiveTab={setActiveTab} |
245 | | - searchQuery={searchQuery} |
246 | | - setSearchQuery={setSearchQuery} |
247 | | - sortOption={sortOption} |
248 | | - setSortOption={setSortOption} |
249 | | - categoryFilter={categoryFilter} |
250 | | - setCategoryFilter={setCategoryFilter} |
251 | | - categories={categories} |
252 | | - /> |
253 | | - |
254 | | - {/* Active Contributions Section */} |
255 | | - {(activeTab === "all" || activeTab === "votes") && ( |
256 | | - <> |
257 | | - <h2 className="text-2xl font-semibold mb-4">Ongoing Contributions</h2> |
258 | | - {isLoadingActive ? ( |
259 | | - <LoadingState type="cards" count={3} /> |
260 | | - ) : ( |
261 | | - <ActiveContributions |
262 | | - projects={sortedActiveProjects} |
263 | | - navigateToProject={navigateToProject} |
264 | | - /> |
265 | | - )} |
266 | | - </> |
267 | | - )} |
268 | | - |
269 | | - {/* Past Contributions Section */} |
270 | | - {(activeTab === "all" || activeTab === "votes") && ( |
271 | | - <> |
272 | | - <h2 className="text-2xl font-semibold mb-4">Past Contributions</h2> |
273 | | - {isLoadingPast ? ( |
274 | | - <LoadingState type="table" count={4} /> |
275 | | - ) : ( |
276 | | - <PastContributions |
277 | | - projects={sortedPastProjects} |
278 | | - navigateToProject={navigateToProject} |
279 | | - /> |
280 | | - )} |
281 | | - </> |
282 | | - )} |
283 | | - |
284 | | - {/* Comments Section */} |
285 | | - {(activeTab === "all" || activeTab === "comments") && ( |
286 | | - <> |
287 | | - <h2 className="text-2xl font-semibold mb-4">My Comments</h2> |
288 | | - {isLoadingComments ? ( |
289 | | - <LoadingState type="comments" count={3} /> |
290 | | - ) : ( |
291 | | - <UserComments |
292 | | - comments={sortedComments} |
293 | | - navigateToProject={navigateToProject} |
294 | | - handleEditComment={handleEditComment} |
295 | | - handleDeleteComment={handleDeleteComment} |
296 | | - /> |
297 | | - )} |
298 | | - </> |
299 | | - )} |
300 | | - |
301 | | - {/* Call-to-Action Section */} |
302 | | - <CallToAction /> |
303 | | - |
304 | | - {/* Modals */} |
305 | | - <CommentEditModal |
306 | | - comment={commentToEdit} |
307 | | - isOpen={!!commentToEdit} |
308 | | - onClose={() => setCommentToEdit(null)} |
309 | | - onSave={saveEditedComment} |
310 | | - /> |
311 | | - |
312 | | - <DeleteConfirmationDialog |
313 | | - isOpen={!!commentToDelete} |
314 | | - onClose={() => setCommentToDelete(null)} |
315 | | - onConfirm={confirmDeleteComment} |
316 | | - /> |
317 | | - </div> |
318 | | - ); |
319 | | -} |
| 9 | +export default Page; |
0 commit comments