Enhance Indexer page with sorting functionality and size/age parsing; update News and Settings pages for improved layout and responsiveness; implement subscription tiers and donation options on Upgrade page.

This commit is contained in:
inhale-dir
2025-05-24 21:54:21 +02:00
parent 1eb9ca54f9
commit 3cd85be0dd
4 changed files with 528 additions and 223 deletions
+292 -66
View File
@@ -1,3 +1,5 @@
'use client'; // Make it a client component
import { Button } from "@/components/ui/button";
import {
Card,
@@ -5,9 +7,12 @@ import {
CardDescription,
CardHeader,
CardTitle,
CardFooter,
} from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import Link from "next/link";
import { CheckCircle2, RadioTower } from "lucide-react"; // Added RadioTower for selection indication
import React, { useState } from "react"; // Import useState
// Placeholder SVG for QR Code
const PlaceholderQrCode = () => (
@@ -25,84 +30,305 @@ const PlaceholderQrCode = () => (
</svg>
);
interface SubscriptionTier {
id: string;
name: string;
priceDisplay: string; // e.g., "Free", "$5"
priceUSD: number; // Numeric price for calculations
priceDescription?: string;
features: string[];
ctaText: string;
isCurrentPlan?: boolean;
highlight?: boolean;
buttonVariant?: "default" | "outline" | "secondary" | "ghost" | "link";
}
const subscriptionTiersData: SubscriptionTier[] = [
{
id: "free",
name: "Basic Access",
priceDisplay: "Free",
priceUSD: 0,
features: [
"5 API hits/day",
"10 Manual downloads/day",
"Access to common categories",
"30-day access",
"Community support",
],
ctaText: "Current Plan",
isCurrentPlan: true,
buttonVariant: "outline",
},
{
id: "pro",
name: "Pro User",
priceDisplay: "$15",
priceUSD: 5,
priceDescription: "/ year",
features: [
"500 API hits/day",
"100 Manual downloads/day",
"Access to all categories",
"Priority support",
"Early access to new features",
],
ctaText: "Select Pro Plan",
highlight: true,
buttonVariant: "default",
},
{
id: "elite",
name: "Elite Contributor",
priceDisplay: "$25",
priceUSD: 10,
priceDescription: "/ year",
features: [
"Unlimited API hits/day",
"Unlimited Manual downloads/day",
"Access to all categories & archives",
"Dedicated VIP support",
"Direct influence on feature roadmap",
"Special badge in community",
],
ctaText: "Select Elite Plan",
buttonVariant: "default",
},
];
// Placeholder conversion rates - replace with actual or dynamic rates if needed
const XMR_PER_USD = 0.005;
const LTC_PER_USD = 0.01;
export default function UpgradePage() {
const [selectedTierId, setSelectedTierId] = useState<string | null>("pro");
const xmrAddress = "4YOURXMRADDRESSXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
const ltcAddress = "LYOURLTCADDRESSXXXXXXXXXXXXXXXXXXXXXXX";
const selectedTier = subscriptionTiersData.find(tier => tier.id === selectedTierId);
const getCryptoAmount = (usdAmount: number, rate: number, applyDiscount: boolean = false) => {
if (usdAmount <= 0) return "N/A";
let finalUsdAmount = usdAmount;
if (applyDiscount) {
finalUsdAmount *= 0.9; // Apply 10% discount
}
return (finalUsdAmount * rate).toFixed(5); // Show 5 decimal places for crypto
};
const isEliteXMRDiscountActive = selectedTier && selectedTier.id === 'elite';
const currentXMRAmount = selectedTier ? getCryptoAmount(selectedTier.priceUSD, XMR_PER_USD, isEliteXMRDiscountActive) : "Select a plan";
const originalXMRAmount = selectedTier ? getCryptoAmount(selectedTier.priceUSD, XMR_PER_USD) : "N/A";
const currentLTCAmount = selectedTier ? getCryptoAmount(selectedTier.priceUSD, LTC_PER_USD) : "Select a plan";
const handleTierSelect = (tierId: string) => {
const tier = subscriptionTiersData.find(t => t.id === tierId);
if (tier && !tier.isCurrentPlan) {
setSelectedTierId(tierId);
} else if (tier && tier.isCurrentPlan) {
setSelectedTierId(null);
}
};
return (
<div className="container mx-auto p-4 py-8 md:py-12 space-y-8">
<div className="flex justify-between items-center mb-6">
<h1 className="text-3xl font-bold tracking-tight">Support & Upgrade</h1>
<div className="container mx-auto p-4 py-8 md:py-12 lg:py-16 xl:max-w-7xl space-y-12 lg:space-y-16">
<div className="flex justify-between items-center mb-6 lg:mb-8">
<h1 className="text-3xl lg:text-4xl font-bold tracking-tight">Support & Upgrade</h1>
<div className="space-x-2">
<Link href="/news" passHref>
<Button variant="ghost">News</Button>
</Link>
<Link href="/settings" passHref>
<Button variant="ghost">Settings</Button>
</Link>
<Link href="/" passHref>
<Button variant="outline">Back to Indexer</Button>
</Link>
<Link href="/news" passHref><Button variant="ghost">News</Button></Link>
<Link href="/settings" passHref><Button variant="ghost">Settings</Button></Link>
<Link href="/" passHref><Button variant="outline">Back to Indexer</Button></Link>
</div>
</div>
<CardDescription className="text-center text-xl text-muted-foreground max-w-2xl mx-auto">
If you find this NZB Indexer useful, please consider supporting its development and maintenance.
Your contributions help keep the servers running and allow us to introduce new features!
</CardDescription>
<div className="grid md:grid-cols-2 gap-10">
{/* Monero (XMR) Card */}
<Card className="shadow-[0_8px_30px_rgb(160,32,240,0.3)] hover:shadow-[0_12px_40px_rgb(160,32,240,0.4)] transition-all duration-300 ease-out">
<CardHeader className="pb-4">
<div className="flex items-center justify-between">
<CardTitle className="text-2xl font-semibold">Monero (XMR)</CardTitle>
{/* Optional: XMR Icon can go here */}
</div>
<CardDescription className="pt-1">
Donate Monero for private, untraceable support.
</CardDescription>
</CardHeader>
<CardContent className="space-y-6 text-center pt-4">
<div className="flex justify-center my-6">
<PlaceholderQrCode />
</div>
<p className="text-sm font-medium text-foreground/90">XMR Address:</p>
<div className="flex items-center space-x-2">
<Input type="text" value={xmrAddress} readOnly className="text-xs bg-background/30 border-foreground/20" />
<Button variant="outline" size="sm" disabled>Copy</Button>
</div>
<p className="text-xs text-muted-foreground pt-2">
Ensure you are sending XMR to this address. Transactions are irreversible.
<section className="space-y-8 lg:space-y-10 p-6 lg:p-8 rounded-xl bg-card/20 shadow-sm">
<div className="text-center">
<h2 className="text-2xl lg:text-3xl font-semibold tracking-tight">Choose Your Plan</h2>
<p className="text-muted-foreground mt-2 lg:mt-3">
Select a plan to proceed with the donation.
</p>
</CardContent>
</Card>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 lg:gap-8">
{subscriptionTiersData.map((tier) => (
<Card
key={tier.id}
className={`flex flex-col cursor-pointer transition-all duration-200
${selectedTierId === tier.id ? 'border-primary ring-2 ring-primary shadow-[0_8px_30px_rgb(var(--primary-hsl)/0.4)]' :
(tier.highlight && !tier.isCurrentPlan && selectedTierId !== 'elite') ? 'border-primary/70 shadow-[0_8px_30px_rgb(var(--primary-hsl)/0.2)]' : 'shadow-md'}
${tier.isCurrentPlan ? 'border-dashed' : ''}
`}
onClick={() => handleTierSelect(tier.id)}
>
<CardHeader className="pb-4 relative">
{selectedTierId === tier.id && (
<RadioTower className="absolute top-3 right-3 h-5 w-5 text-primary" />
)}
<CardTitle className={`text-xl font-semibold ${ (tier.highlight && !tier.isCurrentPlan && selectedTierId !== 'elite') || selectedTierId === tier.id ? 'text-primary' : ''}`}>{tier.name}</CardTitle>
<div className="flex items-baseline">
<span className="text-3xl font-extrabold tracking-tight">{tier.priceDisplay}</span>
{tier.priceDescription && <span className="ml-1 text-sm text-muted-foreground">{tier.priceDescription}</span>}
</div>
</CardHeader>
<CardContent className="flex-grow space-y-3 pt-2">
<ul className="space-y-2">
{tier.features.map((feature, index) => (
<li key={index} className="flex items-center text-sm">
<CheckCircle2 className={`mr-2 h-4 w-4 flex-shrink-0 ${selectedTierId === tier.id || (tier.highlight && !tier.isCurrentPlan && selectedTierId !== 'elite') ? 'text-primary' : 'text-green-500'}`} />
{feature}
</li>
))}
</ul>
</CardContent>
<CardFooter className="mt-auto pt-6">
<Button
className="w-full"
variant={tier.isCurrentPlan ? "outline" : selectedTierId === tier.id ? "default" : (tier.highlight && selectedTierId !== 'elite') ? "default" : "outline"}
disabled={tier.isCurrentPlan}
onClick={(e) => {
if (!tier.isCurrentPlan) {
e.stopPropagation();
handleTierSelect(tier.id);
}
}}
>
{tier.isCurrentPlan ? "Current Plan" : selectedTierId === tier.id ? "Selected" : tier.ctaText}
</Button>
</CardFooter>
</Card>
))}
</div>
<div className="text-center">
<p className="text-muted-foreground mt-2">
{selectedTier
? `To support the ${selectedTier.name} tier (${selectedTier.priceDisplay}${selectedTier.priceDescription || ''}), please donate the equivalent amount shown below.`
: ""}
</p>
</div>
<div className="grid md:grid-cols-2 gap-10">
<Card className={`transition-all duration-300 ease-in-out rounded-lg border hover:border-primary/70
${selectedTier && selectedTier.priceUSD > 0 ? 'border-primary/50 ring-2 ring-primary shadow-[0_10px_35px_rgb(var(--primary-hsl)/0.4)] hover:shadow-[0_12px_40px_rgb(var(--primary-hsl)/0.5)]' : 'border-border/30 opacity-70 hover:opacity-90'}`}>
<CardHeader className="pb-4">
<CardTitle className="text-2xl font-semibold">Monero (XMR)</CardTitle>
<CardDescription className="pt-1">
{selectedTier && selectedTier.priceUSD > 0
? (
<>
{isEliteXMRDiscountActive && (
<span className="block text-xs font-semibold text-green-500 mb-1">
10% DISCOUNT
</span>
)}
Suggested for {selectedTier.name}: ~{currentXMRAmount} XMR
</>
)
: "Donate Monero for private, untraceable support."}
</CardDescription>
</CardHeader>
<CardContent className="space-y-6 text-center pt-4">
<div className="flex justify-center my-6"><PlaceholderQrCode /></div>
<p className="text-sm font-medium text-foreground/90">XMR Address:</p>
<div className="flex items-center space-x-2">
<Input type="text" value={xmrAddress} readOnly className="text-xs bg-background/30 border-foreground/20" />
<Button variant="outline" size="sm" disabled>Copy</Button>
</div>
<p className="text-xs text-muted-foreground pt-2">
Ensure you are sending XMR to this address. Transactions are irreversible.
</p>
</CardContent>
</Card>
<Card className={`transition-all duration-300 ease-in-out rounded-lg border hover:border-primary/70
${selectedTier && selectedTier.priceUSD > 0 ? 'border-primary/50 ring-2 ring-primary shadow-[0_10px_35px_rgb(var(--primary-hsl)/0.4)] hover:shadow-[0_12px_40px_rgb(var(--primary-hsl)/0.5)]' : 'border-border/30 opacity-70 hover:opacity-90'}`}>
<CardHeader className="pb-4">
<CardTitle className="text-2xl font-semibold">Litecoin (LTC)</CardTitle>
<CardDescription className="pt-1">
{selectedTier && selectedTier.priceUSD > 0
? `Suggested for ${selectedTier.name}: ~${currentLTCAmount} LTC`
: "Donate Litecoin for fast and low-fee transactions."}
</CardDescription>
</CardHeader>
<CardContent className="space-y-6 text-center pt-4">
<div className="flex justify-center my-6"><PlaceholderQrCode /></div>
<p className="text-sm font-medium text-foreground/90">LTC Address:</p>
<div className="flex items-center space-x-2">
<Input type="text" value={ltcAddress} readOnly className="text-xs bg-background/30 border-foreground/20" />
<Button variant="outline" size="sm" disabled>Copy</Button>
</div>
<p className="text-xs text-muted-foreground pt-2">
Ensure you are sending LTC to this address. Transactions are irreversible.
</p>
</CardContent>
</Card>
</div>
</section>
{/* Litecoin (LTC) Card */}
<Card className="shadow-[0_8px_30px_rgb(56,189,248,0.3)] hover:shadow-[0_12px_40px_rgb(56,189,248,0.4)] transition-all duration-300 ease-out">
<CardHeader className="pb-4">
<div className="flex items-center justify-between">
<CardTitle className="text-2xl font-semibold">Litecoin (LTC)</CardTitle>
{/* Optional: LTC Icon can go here */}
</div>
<CardDescription className="pt-1">
Donate Litecoin for fast and low-fee transactions.
</CardDescription>
</CardHeader>
<CardContent className="space-y-6 text-center pt-4">
<div className="flex justify-center my-6">
<PlaceholderQrCode />
</div>
<p className="text-sm font-medium text-foreground/90">LTC Address:</p>
<div className="flex items-center space-x-2">
<Input type="text" value={ltcAddress} readOnly className="text-xs bg-background/30 border-foreground/20" />
<Button variant="outline" size="sm" disabled>Copy</Button>
</div>
<p className="text-xs text-muted-foreground pt-2">
Ensure you are sending LTC to this address. Transactions are irreversible.
<section className="space-y-8 lg:space-y-10 p-6 lg:p-8 rounded-xl bg-card/20 shadow-sm">
<div className="text-center pb-4 border-b border-border/50 mb-6 lg:mb-8" >
<h2 className="text-2xl lg:text-3xl font-semibold tracking-tight">Or, Make a One-Time Donation</h2>
<p className="text-muted-foreground mt-2 lg:mt-3">
{selectedTier && selectedTier.priceUSD > 0
? `You've selected the ${selectedTier.name} plan. Suggested donation amounts are shown below.`
: "Support our project with a custom crypto donation." }
</p>
</CardContent>
</Card>
</div>
</div>
<div className="grid md:grid-cols-2 lg:grid-cols-2 gap-8 lg:gap-12 xl:gap-16">
<Card className={`transition-all duration-300 ease-in-out rounded-lg border hover:border-primary/70
${selectedTier && selectedTier.priceUSD > 0 ? 'border-primary/50 ring-2 ring-primary shadow-[0_10px_35px_rgb(var(--primary-hsl)/0.4)] hover:shadow-[0_12px_40px_rgb(var(--primary-hsl)/0.5)]' : 'border-border/30 opacity-70 hover:opacity-90'}`}>
<CardHeader className="pb-4">
<CardTitle className="text-2xl font-semibold">Monero (XMR)</CardTitle>
<CardDescription className="pt-1">
{selectedTier && selectedTier.priceUSD > 0
? (
<>
{isEliteXMRDiscountActive && (
<span className="block text-xs font-semibold text-green-500 mb-1">
10% DISCOUNT
</span>
)}
Suggested for {selectedTier.name}: ~{currentXMRAmount} XMR
</>
)
: "Donate Monero for private, untraceable support."}
</CardDescription>
</CardHeader>
<CardContent className="space-y-6 text-center pt-4">
<div className="flex justify-center my-6"><PlaceholderQrCode /></div>
<p className="text-sm font-medium text-foreground/90">XMR Address:</p>
<div className="flex items-center space-x-2">
<Input type="text" value={xmrAddress} readOnly className="text-xs bg-background/30 border-foreground/20" />
<Button variant="outline" size="sm" disabled>Copy</Button>
</div>
<p className="text-xs text-muted-foreground pt-2">
Ensure you are sending XMR to this address. Transactions are irreversible.
</p>
</CardContent>
</Card>
<Card className={`transition-all duration-300 ease-in-out rounded-lg border hover:border-primary/70
${selectedTier && selectedTier.priceUSD > 0 ? 'border-primary/50 ring-2 ring-primary shadow-[0_10px_35px_rgb(var(--primary-hsl)/0.4)] hover:shadow-[0_12px_40px_rgb(var(--primary-hsl)/0.5)]' : 'border-border/30 opacity-70 hover:opacity-90'}`}>
<CardHeader className="pb-4">
<CardTitle className="text-2xl font-semibold">Litecoin (LTC)</CardTitle>
<CardDescription className="pt-1">
{selectedTier && selectedTier.priceUSD > 0
? `Suggested for ${selectedTier.name}: ~${currentLTCAmount} LTC`
: "Donate Litecoin for fast and low-fee transactions."}
</CardDescription>
</CardHeader>
<CardContent className="space-y-6 text-center pt-4">
<div className="flex justify-center my-6"><PlaceholderQrCode /></div>
<p className="text-sm font-medium text-foreground/90">LTC Address:</p>
<div className="flex items-center space-x-2">
<Input type="text" value={ltcAddress} readOnly className="text-xs bg-background/30 border-foreground/20" />
<Button variant="outline" size="sm" disabled>Copy</Button>
</div>
<p className="text-xs text-muted-foreground pt-2">
Ensure you are sending LTC to this address. Transactions are irreversible.
</p>
</CardContent>
</Card>
</div>
</section>
</div>
);
}