Added CurseForge edge proxy
Co-authored-by: felix-fx-top <253056634+felix-fx-top@users.noreply.github.com>
This commit is contained in:
parent
fc64d3552c
commit
859c93fc4b
@ -122,6 +122,7 @@ const ModGrid = ({
|
|||||||
categories={mod.categories || []}
|
categories={mod.categories || []}
|
||||||
projectType={mod.project_type}
|
projectType={mod.project_type}
|
||||||
viewMode={viewMode}
|
viewMode={viewMode}
|
||||||
|
source={mod.source}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
159
src/pages/CurseForgeDetails.tsx
Normal file
159
src/pages/CurseForgeDetails.tsx
Normal file
@ -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 (
|
||||||
|
<div className="flex min-h-screen flex-col">
|
||||||
|
<Navbar />
|
||||||
|
<main className="flex-1">
|
||||||
|
<div className="container mx-auto px-4 py-8">
|
||||||
|
{loadingMod ? (
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="flex gap-6">
|
||||||
|
<Skeleton className="h-24 w-24 rounded-xl" />
|
||||||
|
<div className="flex-1 space-y-3">
|
||||||
|
<Skeleton className="h-8 w-1/3" />
|
||||||
|
<Skeleton className="h-4 w-2/3" />
|
||||||
|
<Skeleton className="h-4 w-1/2" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : project ? (
|
||||||
|
<>
|
||||||
|
<div className="mb-8 flex flex-col gap-6 md:flex-row">
|
||||||
|
<div className="h-24 w-24 shrink-0 overflow-hidden rounded-xl bg-secondary">
|
||||||
|
{project.logo?.url ? (
|
||||||
|
<img src={project.logo.url} alt={project.name} className="h-full w-full object-cover" />
|
||||||
|
) : (
|
||||||
|
<div className="flex h-full w-full items-center justify-center text-4xl">🟠</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<div className="mb-2 flex items-center gap-2">
|
||||||
|
<h1 className="text-3xl font-black">{project.name}</h1>
|
||||||
|
<Badge variant="outline" className="text-orange-400 border-orange-400">CurseForge</Badge>
|
||||||
|
</div>
|
||||||
|
<p className="mb-4 text-muted-foreground">{project.summary}</p>
|
||||||
|
<div className="flex flex-wrap items-center gap-4 text-sm text-muted-foreground">
|
||||||
|
<span className="flex items-center gap-1">
|
||||||
|
<Download className="h-4 w-4" />
|
||||||
|
{formatNumber(project.downloadCount)} تحميل
|
||||||
|
</span>
|
||||||
|
<span className="flex items-center gap-1">
|
||||||
|
<Heart className="h-4 w-4" />
|
||||||
|
{formatNumber(project.thumbsUpCount || 0)} إعجاب
|
||||||
|
</span>
|
||||||
|
<span className="flex items-center gap-1">
|
||||||
|
<Calendar className="h-4 w-4" />
|
||||||
|
{formatDate(project.dateCreated)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="mt-3 flex flex-wrap gap-2">
|
||||||
|
{project.categories?.map((cat: any) => (
|
||||||
|
<Badge key={cat.id} variant="secondary">{cat.name}</Badge>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Files / Versions */}
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>الملفات</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
{loadingFiles ? (
|
||||||
|
<div className="space-y-3">
|
||||||
|
{Array.from({ length: 3 }).map((_, i) => (
|
||||||
|
<Skeleton key={i} className="h-16 w-full" />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : files.length > 0 ? (
|
||||||
|
<div className="space-y-3">
|
||||||
|
{files.slice(0, 10).map((file: any) => (
|
||||||
|
<div
|
||||||
|
key={file.id}
|
||||||
|
className="flex flex-col gap-3 rounded-lg border border-border bg-secondary/50 p-4 sm:flex-row sm:items-center sm:justify-between"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div className="mb-1 flex items-center gap-2">
|
||||||
|
<span className="font-bold">{file.displayName}</span>
|
||||||
|
<Badge variant="outline" className="text-xs">
|
||||||
|
{file.releaseType === 1 ? "release" : file.releaseType === 2 ? "beta" : "alpha"}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-wrap gap-2 text-xs text-muted-foreground">
|
||||||
|
{file.gameVersions?.slice(0, 5).map((gv: string) => (
|
||||||
|
<span key={gv}>{gv}</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{file.downloadUrl && (
|
||||||
|
<Button asChild size="sm" className="gap-1 shrink-0">
|
||||||
|
<a href={file.downloadUrl} target="_blank" rel="noopener noreferrer">
|
||||||
|
<Download className="h-4 w-4" />
|
||||||
|
تحميل
|
||||||
|
</a>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<p className="text-muted-foreground">لا توجد ملفات متاحة</p>
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<div className="py-20 text-center text-muted-foreground">
|
||||||
|
الإضافة غير موجودة
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CurseForgeDetails;
|
||||||
Loading…
x
Reference in New Issue
Block a user