225 lines
8.2 KiB
TypeScript
225 lines
8.2 KiB
TypeScript
"use client";
|
||
|
||
import { useState, useEffect } from "react";
|
||
import { motion, Variants } from "framer-motion";
|
||
import { Button } from "@/components/ui/button";
|
||
import {
|
||
Monitor,
|
||
Smartphone,
|
||
Terminal,
|
||
Apple,
|
||
Download,
|
||
ExternalLink,
|
||
Bell,
|
||
Loader2,
|
||
Clock,
|
||
} from "lucide-react";
|
||
import { Dictionary } from "@/lib/IDict";
|
||
import { pb, type PBDownload, getPbFileUrl } from "@/lib/pb";
|
||
|
||
interface DownloadClientProps {
|
||
dict: Dictionary["download"];
|
||
lang: string;
|
||
}
|
||
|
||
export function DownloadClient({ dict, lang }: DownloadClientProps) {
|
||
const [platforms, setPlatforms] = useState<PBDownload[]>([]);
|
||
const [loading, setLoading] = useState(true);
|
||
|
||
useEffect(() => {
|
||
async function fetchDownloads() {
|
||
try {
|
||
setLoading(true);
|
||
const records = await pb
|
||
.collection(`downloads_${lang}`)
|
||
.getFullList<PBDownload>({
|
||
sort: "order",
|
||
requestKey: null,
|
||
});
|
||
setPlatforms(records);
|
||
} catch (error) {
|
||
console.error("Ошибка загрузки данных платформы:", error);
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
}
|
||
fetchDownloads();
|
||
}, [lang]);
|
||
|
||
// Единый конфиг стилей: Primary (Фиолетовый) для базы, Secondary (Циан) для акцентов
|
||
const getPlatformIcon = (platformId: string) => {
|
||
const props = {
|
||
className:
|
||
"size-8 text-secondary drop-shadow-[0_0_10px_rgba(0,229,242,0.8)]",
|
||
};
|
||
switch (platformId) {
|
||
case "windows":
|
||
return <Monitor {...props} />;
|
||
case "linux":
|
||
return <Terminal {...props} />;
|
||
case "android":
|
||
return <Smartphone {...props} />;
|
||
case "macos":
|
||
case "ios":
|
||
return <Apple {...props} />;
|
||
default:
|
||
return <Download {...props} />;
|
||
}
|
||
};
|
||
|
||
const containerVariants: Variants = {
|
||
hidden: { opacity: 0 },
|
||
show: {
|
||
opacity: 1,
|
||
transition: { staggerChildren: 0.1 },
|
||
},
|
||
};
|
||
|
||
const itemVariants: Variants = {
|
||
hidden: { opacity: 0, y: 40 },
|
||
show: {
|
||
opacity: 1,
|
||
y: 0,
|
||
transition: { type: "spring", stiffness: 300, damping: 24 },
|
||
},
|
||
};
|
||
|
||
return (
|
||
<div className="min-h-screen pt-32 pb-24 px-4 container mx-auto max-w-5xl relative">
|
||
{/* Мягкое фоновое свечение */}
|
||
<div className="absolute top-1/4 left-1/2 -translate-x-1/2 w-full max-w-[800px] h-[400px] bg-primary/5 blur-[120px] rounded-full pointer-events-none -z-10" />
|
||
|
||
<div className="text-center mb-20">
|
||
<h1 className="text-4xl md:text-6xl font-black tracking-tighter mb-6 text-foreground">
|
||
{dict.title}
|
||
</h1>
|
||
<p className="text-muted-foreground text-lg max-w-2xl mx-auto font-medium">
|
||
{dict.subtitle}
|
||
</p>
|
||
</div>
|
||
|
||
{loading ? (
|
||
<div className="flex flex-col items-center justify-center py-20 text-primary opacity-50">
|
||
<Loader2 className="w-10 h-10 animate-spin mb-4" />
|
||
<span className="font-mono text-xs uppercase tracking-[0.3em]">
|
||
Accessing_Build_Server...
|
||
</span>
|
||
</div>
|
||
) : (
|
||
<motion.div
|
||
variants={containerVariants}
|
||
initial="hidden"
|
||
animate="show"
|
||
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 md:gap-8"
|
||
>
|
||
{platforms.map((platform) => (
|
||
<motion.div
|
||
key={platform.id}
|
||
variants={itemVariants}
|
||
className={`bg-card/40 backdrop-blur-xl border border-border/50 rounded-[2.5rem] p-8 md:p-10 flex flex-col transition-all duration-500 relative overflow-hidden group hover:border-primary/40 hover:shadow-[0_0_40px_rgba(139,61,255,0.15)] ${
|
||
platform.status === "dev" ? "opacity-60 grayscale-[50%]" : ""
|
||
}`}
|
||
>
|
||
{/* Статус бейдж */}
|
||
<div className="absolute top-8 right-8">
|
||
{platform.status === "active" ? (
|
||
<div className="px-3 py-1 rounded-full bg-primary/10 border border-primary/20 text-primary text-[10px] font-bold uppercase tracking-widest shadow-sm">
|
||
{dict.badges.active}
|
||
</div>
|
||
) : (
|
||
<div className="px-3 py-1 rounded-full bg-muted border border-border text-muted-foreground text-[10px] font-bold uppercase tracking-widest">
|
||
{dict.badges.dev}
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
{/* Иконка в Циановом (Secondary) стиле */}
|
||
<div className="mb-10 p-5 rounded-[1.5rem] bg-secondary/10 inline-flex border border-secondary/20 shadow-[0_0_20px_rgba(0,229,242,0.15)] w-fit transition-transform duration-500 group-hover:scale-110">
|
||
{getPlatformIcon(platform.platformId)}
|
||
</div>
|
||
|
||
<div className="mb-10">
|
||
<h3 className="text-2xl font-bold text-foreground mb-2 tracking-tight">
|
||
{platform.name}
|
||
</h3>
|
||
<p className="text-sm text-muted-foreground font-mono">
|
||
{platform.version}
|
||
</p>
|
||
</div>
|
||
|
||
{/* Кнопки действий: Основная всегда Фиолетовая (Primary) */}
|
||
<div className="mt-auto flex flex-col gap-3">
|
||
{platform.status === "active" ? (
|
||
<>
|
||
{/* Прямое скачивание */}
|
||
{platform.file ? (
|
||
<Button
|
||
asChild
|
||
className="w-full h-14 rounded-2xl font-bold gap-3 bg-primary text-primary-foreground hover:bg-primary/90 shadow-[0_0_20px_rgba(139,61,255,0.3)] transition-all active:scale-[0.98]"
|
||
>
|
||
<a
|
||
href={getPbFileUrl(platform, platform.file)}
|
||
download
|
||
>
|
||
<Download className="size-5" />
|
||
{dict.buttons.direct}
|
||
</a>
|
||
</Button>
|
||
) : (
|
||
<Button
|
||
disabled
|
||
variant="outline"
|
||
className="w-full h-14 rounded-2xl font-bold gap-3 opacity-50 bg-background/50 border-dashed"
|
||
>
|
||
<Download className="size-5" />
|
||
Build Pending
|
||
</Button>
|
||
)}
|
||
|
||
{/* Ссылка на магазин (Вместо GitHub) */}
|
||
{platform.storeLink ? (
|
||
<Button
|
||
asChild
|
||
variant="outline"
|
||
className="w-full h-14 rounded-2xl gap-3 border-border/50 bg-background/30 text-muted-foreground hover:text-foreground hover:border-primary/30 transition-all"
|
||
>
|
||
<a
|
||
href={platform.storeLink}
|
||
target="_blank"
|
||
rel="noopener noreferrer"
|
||
>
|
||
<ExternalLink className="size-4" />
|
||
{dict.buttons.store}
|
||
</a>
|
||
</Button>
|
||
) : (
|
||
<Button
|
||
variant="outline"
|
||
disabled
|
||
className="w-full h-14 rounded-2xl gap-3 border-border/20 bg-background/20 text-muted-foreground/40 cursor-not-allowed"
|
||
>
|
||
<Clock className="size-4" />
|
||
{dict.buttons.soon}
|
||
</Button>
|
||
)}
|
||
</>
|
||
) : (
|
||
// В разработке
|
||
<Button
|
||
variant="secondary"
|
||
disabled
|
||
className="w-full h-14 rounded-2xl gap-3 opacity-40 cursor-not-allowed bg-muted"
|
||
>
|
||
<Bell className="size-4" />
|
||
{dict.buttons.notify}
|
||
</Button>
|
||
)}
|
||
</div>
|
||
</motion.div>
|
||
))}
|
||
</motion.div>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|