From 859c93fc4b4d7bf857805725322c0de97e46b711 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Tue, 31 Mar 2026 10:37:45 +0000 Subject: [PATCH] Added CurseForge edge proxy Co-authored-by: felix-fx-top <253056634+felix-fx-top@users.noreply.github.com> --- src/components/ModGrid.tsx | 1 + src/pages/CurseForgeDetails.tsx | 159 ++++++++++++++++++++++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 src/pages/CurseForgeDetails.tsx diff --git a/src/components/ModGrid.tsx b/src/components/ModGrid.tsx index 1c8bfc3..2d9bd30 100644 --- a/src/components/ModGrid.tsx +++ b/src/components/ModGrid.tsx @@ -122,6 +122,7 @@ const ModGrid = ({ categories={mod.categories || []} projectType={mod.project_type} viewMode={viewMode} + source={mod.source} /> ))} diff --git a/src/pages/CurseForgeDetails.tsx b/src/pages/CurseForgeDetails.tsx new file mode 100644 index 0000000..35b3a5d --- /dev/null +++ b/src/pages/CurseForgeDetails.tsx @@ -0,0 +1,159 @@ +import { useParams } from "react-router-dom"; +import { useQuery } from "@tanstack/react-query"; +import Navbar from "@/components/Navbar"; +import Footer from "@/components/Footer"; +import { Button } from "@/components/ui/button"; +import { Badge } from "@/components/ui/badge"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Skeleton } from "@/components/ui/skeleton"; +import { Download, Heart, Calendar } from "lucide-react"; +import { getCurseForgeMod, getCurseForgeFiles } from "@/lib/api"; + +const formatNumber = (num: number) => { + if (num >= 1000000) return (num / 1000000).toFixed(1) + "M"; + if (num >= 1000) return (num / 1000).toFixed(1) + "K"; + return num.toString(); +}; + +const formatDate = (dateStr: string) => { + return new Date(dateStr).toLocaleDateString("ar-SA", { + year: "numeric", + month: "long", + day: "numeric", + }); +}; + +const CurseForgeDetails = () => { + const { id } = useParams<{ id: string }>(); + + const { data: modData, isLoading: loadingMod } = useQuery({ + queryKey: ["cf-mod", id], + queryFn: () => getCurseForgeMod(id!), + enabled: !!id, + }); + + const { data: filesData, isLoading: loadingFiles } = useQuery({ + queryKey: ["cf-files", id], + queryFn: () => getCurseForgeFiles(id!), + enabled: !!id, + }); + + const project = modData?.data; + const files = filesData?.data || []; + + return ( +
+ +
+
+ {loadingMod ? ( +
+
+ +
+ + + +
+
+
+ ) : project ? ( + <> +
+
+ {project.logo?.url ? ( + {project.name} + ) : ( +
🟠
+ )} +
+
+
+

{project.name}

+ CurseForge +
+

{project.summary}

+
+ + + {formatNumber(project.downloadCount)} تحميل + + + + {formatNumber(project.thumbsUpCount || 0)} إعجاب + + + + {formatDate(project.dateCreated)} + +
+
+ {project.categories?.map((cat: any) => ( + {cat.name} + ))} +
+
+
+ + {/* Files / Versions */} + + + الملفات + + + {loadingFiles ? ( +
+ {Array.from({ length: 3 }).map((_, i) => ( + + ))} +
+ ) : files.length > 0 ? ( +
+ {files.slice(0, 10).map((file: any) => ( +
+
+
+ {file.displayName} + + {file.releaseType === 1 ? "release" : file.releaseType === 2 ? "beta" : "alpha"} + +
+
+ {file.gameVersions?.slice(0, 5).map((gv: string) => ( + {gv} + ))} +
+
+ {file.downloadUrl && ( + + )} +
+ ))} +
+ ) : ( +

لا توجد ملفات متاحة

+ )} +
+
+ + ) : ( +
+ الإضافة غير موجودة +
+ )} +
+
+
+ ); +}; + +export default CurseForgeDetails;