init commit

This commit is contained in:
inhale-dir 2025-05-23 23:08:13 +02:00
parent 7ddffa1afe
commit 91b4b5802f
18 changed files with 2193 additions and 232 deletions

View File

@ -2,20 +2,105 @@
@tailwind components;
@tailwind utilities;
:root {
--background: #ffffff;
--foreground: #171717;
}
@media (prefers-color-scheme: dark) {
@layer base {
:root {
--background: #0a0a0a;
--foreground: #ededed;
--background: 0 0% 100%;
--foreground: 0 0% 3.9%;
--card: 0 0% 100%;
--card-foreground: 0 0% 3.9%;
--popover: 0 0% 100%;
--popover-foreground: 0 0% 3.9%;
--primary: 0 0% 9%;
--primary-foreground: 0 0% 98%;
--secondary: 0 0% 96.1%;
--secondary-foreground: 0 0% 9%;
--muted: 0 0% 96.1%;
--muted-foreground: 0 0% 45.1%;
--accent: 0 0% 96.1%;
--accent-foreground: 0 0% 9%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 89.8%;
--input: 0 0% 89.8%;
--ring: 0 0% 3.9%;
--chart-1: 12 76% 61%;
--chart-2: 173 58% 39%;
--chart-3: 197 37% 24%;
--chart-4: 43 74% 66%;
--chart-5: 27 87% 67%;
--radius: 0.5rem
}
.dark {
--background: 240 10% 4%; /* Very dark blue-black */
--foreground: 240 10% 95%; /* Light, slightly bluish white */
--card: 240 10% 7%; /* Slightly lighter dark blue-black */
--card-foreground: 240 10% 95%;
--popover: 240 10% 7%;
--popover-foreground: 240 10% 95%;
--primary: 270 70% 55%; /* Main purple */
--primary-foreground: 270 70% 95%; /* Light purple for text on primary */
--secondary: 270 40% 25%; /* Darker, muted purple */
--secondary-foreground: 270 40% 85%; /* Lighter muted purple for text */
--muted: 240 10% 15%; /* Dark gray */
--muted-foreground: 240 5% 60%; /* Lighter gray for muted text */
--accent: 270 80% 65%; /* Brighter purple for accents */
--accent-foreground: 270 80% 95%; /* Light purple for text on accent */
--destructive: 0 62.8% 30.6%; /* Keeping default red for destructive actions */
--destructive-foreground: 0 0% 98%;
--border: 240 10% 15%; /* Dark gray border */
--input: 240 10% 15%; /* Dark gray input background */
--ring: 270 70% 60%; /* Purple for focus rings */
--chart-1: 270 70% 50%;
--chart-2: 270 60% 55%;
--chart-3: 270 50% 60%;
--chart-4: 270 40% 65%;
--chart-5: 270 30% 70%;
}
}
body {
color: var(--foreground);
background: var(--background);
font-family: Arial, Helvetica, sans-serif;
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}

View File

@ -23,7 +23,7 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
return (
<html lang="en">
<html lang="en" className="dark">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>

111
app/news/page.tsx Normal file
View File

@ -0,0 +1,111 @@
'use client'; // Or remove if no client-side interactivity needed for display
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import Link from "next/link";
import React from "react";
interface NewsArticle {
id: string;
title: string;
date: string;
author: string;
content: string; // Can be a snippet or full content
isFullContent?: boolean; // Optional: to toggle between snippet and full
}
const mockNewsData: NewsArticle[] = [
{
id: "news1",
title: "Important: Upcoming Maintenance Window",
date: "October 26, 2023",
author: "Admin Team",
content: "Please be advised that we will be performing scheduled maintenance on our servers this Saturday from 02:00 to 04:00 UTC. During this time, the indexer may be temporarily unavailable. We apologize for any inconvenience.",
},
{
id: "news2",
title: "New Feature Alert: Dark Mode Enhanced!",
date: "October 24, 2023",
author: "Dev Team",
content: "We're excited to announce that our dark mode has been revamped with even better contrast and new thematic colors. Check it out in your settings and let us know what you think! More UI enhancements are on the way.",
},
{
id: "news3",
title: "Welcome to the New NZB Indexer!",
date: "October 20, 2023",
author: "Admin",
content: "Welcome everyone to the newly launched NZB Indexer! We're thrilled to have you here. Explore the features, and feel free to reach out with any feedback or suggestions. Happy indexing!",
},
];
export default function NewsPage() {
// For now, we just display all content. Could add a toggle later.
// const [expandedArticles, setExpandedArticles] = useState<Set<string>>(new Set());
// const toggleReadMore = (id: string) => {
// setExpandedArticles(prev => {
// const newSet = new Set(prev);
// if (newSet.has(id)) {
// newSet.delete(id);
// } else {
// newSet.add(id);
// }
// return newSet;
// });
// };
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">Site News & Announcements</h1>
<div className="space-x-2">
<Link href="/upgrade" passHref>
<Button variant="ghost">Upgrade</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>
<div className="space-y-6">
{mockNewsData.map((article) => (
<Card key={article.id} className="shadow-md hover:shadow-lg transition-shadow duration-200">
<CardHeader>
<CardTitle>{article.title}</CardTitle>
<CardDescription>
Posted by {article.author} on {article.date}
</CardDescription>
</CardHeader>
<CardContent>
<p className="text-sm text-foreground/90 whitespace-pre-line">
{article.content}
{/* {article.content.length > 200 && !expandedArticles.has(article.id)
? `${article.content.substring(0, 200)}...`
: article.content}
*/}
</p>
</CardContent>
{/* {article.content.length > 200 && (
<CardFooter>
<Button variant="link" onClick={() => toggleReadMore(article.id)} className="p-0 h-auto">
{expandedArticles.has(article.id) ? "Read Less" : "Read More"}
</Button>
</CardFooter>
)} */}
</Card>
))}
</div>
</div>
);
}

View File

@ -1,101 +1,308 @@
import Image from "next/image";
'use client';
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import Link from "next/link";
import {
Table,
TableBody,
TableCaption,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogClose,
} from "@/components/ui/dialog";
import {
Carousel,
CarouselContent,
CarouselItem,
} from "@/components/ui/carousel";
import Autoplay from "embla-carousel-autoplay";
import { Card, CardContent } from "@/components/ui/card";
import React, { useState, useRef } from "react";
// Mock data structure
interface NzbItem {
id: string;
name: string;
size: string;
age: string;
category: string;
downloads: number;
metadata: {
description: string;
groups: string[];
files: string[];
};
}
// Mock data structure for Carousel items
interface FeaturedItem {
id: string;
title: string;
category: string;
imageUrl?: string; // Optional: direct image URL or use a placeholder
description: string;
rating: string; // e.g., "IMDb: 8.5/10"
}
// Placeholder SVG for Carousel Item Image
const PlaceholderCarouselImage = ({ text }: { text: string }) => (
<div className="w-full h-64 bg-muted flex items-center justify-center rounded-md">
<span className="text-xl text-muted-foreground">{text}</span>
</div>
);
export default function IndexerPage() {
const categories = ["All", "Movies", "Series", "Anime", "Music"];
const [selectedItem, setSelectedItem] = useState<NzbItem | null>(null);
// Autoplay plugin ref
const autoplayPlugin = useRef(
Autoplay({ delay: 10000, stopOnInteraction: true })
);
const mockFeaturedData: FeaturedItem[] = [
{
id: "feat1",
title: "Blockbuster Movie Premiere",
category: "Movies",
description: "The latest action-packed thriller hits the screens! Don't miss out.",
rating: "IMDb: 9.2/10",
},
{
id: "feat2",
title: "New Hit Series - Episode 1",
category: "Series",
description: "A gripping new drama series that will keep you on the edge of your seat.",
rating: "Rotten Tomatoes: 95%",
},
{
id: "feat3",
title: "Must-Watch Anime Film",
category: "Anime",
description: "Critically acclaimed anime feature with stunning visuals.",
rating: "MyAnimeList: 8.9/10",
},
];
const mockNzbData: NzbItem[] = [
{
id: "1",
name: "Example Movie Title",
size: "2.5 GB",
age: "1 day",
category: "Movies",
downloads: 1250,
metadata: {
description: "A fantastic movie about something exciting.",
groups: ["alt.binaries.movies.hd"],
files: ["movie_part1.rar", "movie_part2.rar"],
},
},
{
id: "2",
name: "Awesome Series S01E01",
size: "500 MB",
age: "5 hours",
category: "Series",
downloads: 340,
metadata: {
description: "First episode of an awesome new series.",
groups: ["alt.binaries.tv"],
files: ["series_s01e01.mkv"],
},
},
{
id: "3",
name: "Cool Anime Movie",
size: "1.2 GB",
age: "3 days",
category: "Anime",
downloads: 780,
metadata: {
description: "A visually stunning anime film.",
groups: ["alt.binaries.anime"],
files: ["anime.movie.mkv"],
},
},
{
id: "4",
name: "Great Music Album",
size: "300 MB",
age: "10 days",
category: "Music",
downloads: 50,
metadata: {
description: "An album by a popular artist.",
groups: ["alt.binaries.music.mp3"],
files: ["track01.mp3", "track02.mp3", "cover.jpg"],
},
},
];
// Filter data based on category (simple filter for now)
const getFilteredData = (category: string) => {
if (category === "All") return mockNzbData;
return mockNzbData.filter((item) => item.category === category);
};
export default function Home() {
return (
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
<main className="flex flex-col gap-8 row-start-2 items-center sm:items-start">
<Image
className="dark:invert"
src="/next.svg"
alt="Next.js logo"
width={180}
height={38}
priority
/>
<ol className="list-inside list-decimal text-sm text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
<li className="mb-2">
Get started by editing{" "}
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-semibold">
app/page.tsx
</code>
.
</li>
<li>Save and see your changes instantly.</li>
</ol>
<div className="flex gap-4 items-center flex-col sm:flex-row">
<a
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5"
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
className="dark:invert"
src="/vercel.svg"
alt="Vercel logomark"
width={20}
height={20}
/>
Deploy now
</a>
<a
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44"
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Read our docs
</a>
<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">NZB Indexer</h1>
<div className="space-x-2">
<Link href="/news" passHref>
<Button variant="ghost">News</Button>
</Link>
<Link href="/upgrade" passHref>
<Button variant="ghost">Upgrade</Button>
</Link>
<Link href="/settings" passHref>
<Button variant="ghost">Settings</Button>
</Link>
</div>
</main>
<footer className="row-start-3 flex gap-6 flex-wrap items-center justify-center">
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
</div>
{/* Featured Carousel Section */}
<div className="space-y-4">
<h2
className="text-2xl font-semibold tracking-tight text-primary drop-shadow-[0_2px_3px_hsl(var(--primary)/0.5)]"
>
<Image
aria-hidden
src="/file.svg"
alt="File icon"
width={16}
height={16}
/>
Learn
</a>
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
Newest Uploads
</h2>
<Carousel
className="w-full"
opts={{ loop: true }}
plugins={[autoplayPlugin.current]}
onMouseEnter={autoplayPlugin.current.stop}
onMouseLeave={autoplayPlugin.current.reset}
>
<Image
aria-hidden
src="/window.svg"
alt="Window icon"
width={16}
height={16}
/>
Examples
</a>
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/globe.svg"
alt="Globe icon"
width={16}
height={16}
/>
Go to nextjs.org
</a>
</footer>
<CarouselContent>
{mockFeaturedData.map((featuredItem) => (
<CarouselItem key={featuredItem.id}>
<Card className="overflow-hidden">
<CardContent className="flex flex-col md:flex-row items-center p-0">
<div className="w-full md:w-1/3 h-64 md:h-auto">
<PlaceholderCarouselImage text={featuredItem.title} />
</div>
<div className="p-6 space-y-3 flex-1">
<h3 className="text-xl font-semibold">{featuredItem.title}</h3>
<p className="text-sm text-muted-foreground">{featuredItem.description}</p>
<div className="flex justify-between items-center pt-2">
<span className="text-sm font-medium text-primary">{featuredItem.rating}</span>
<Button variant="secondary" size="sm">View Details</Button>
</div>
</div>
</CardContent>
</Card>
</CarouselItem>
))}
</CarouselContent>
</Carousel>
</div>
<Tabs defaultValue="All" className="w-full">
<TabsList className="mb-4">
{categories.map((category) => (
<TabsTrigger key={category} value={category}>
{category}
</TabsTrigger>
))}
</TabsList>
{categories.map((category) => (
<TabsContent key={category} value={category}>
<div className="flex w-full max-w-sm items-center space-x-2 mb-4">
<Input type="text" placeholder={`Search in ${category}...`} />
<Button type="submit">Search</Button>
</div>
<Table>
<TableCaption>
{category === "All"
? "A list of NZBs."
: `Results for ${category}.`}
</TableCaption>
<TableHeader>
<TableRow>
<TableHead className="w-[35%]">Name</TableHead>
<TableHead>Size</TableHead>
<TableHead>Age</TableHead>
<TableHead>Downloads</TableHead>
<TableHead className="text-right">Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{getFilteredData(category).map((item) => (
<TableRow key={item.id}>
<TableCell className="font-medium">{item.name}</TableCell>
<TableCell>{item.size}</TableCell>
<TableCell>{item.age}</TableCell>
<TableCell>{item.downloads.toLocaleString()}</TableCell>
<TableCell className="text-right space-x-2">
<Button variant="ghost" size="sm" onClick={() => setSelectedItem(item)}>
Info
</Button>
<Button variant="outline" size="sm">
Download
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TabsContent>
))}
</Tabs>
{/* Dialog for NZB Info - controlled by selectedItem state */}
{selectedItem && (
<Dialog open={!!selectedItem} onOpenChange={(isOpen) => !isOpen && setSelectedItem(null)}>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle>{selectedItem.name}</DialogTitle>
<DialogDescription>
Category: {selectedItem.category} | Size: {selectedItem.size} | Age: {selectedItem.age} | Downloads: {selectedItem.downloads.toLocaleString()}
</DialogDescription>
</DialogHeader>
<div className="py-4 space-y-2">
<h4 className="font-semibold">Description:</h4>
<p className="text-sm text-muted-foreground">
{selectedItem.metadata.description}
</p>
<h4 className="font-semibold">Groups:</h4>
<ul className="list-disc list-inside text-sm text-muted-foreground">
{selectedItem.metadata.groups.map((group) => (
<li key={group}>{group}</li>
))}
</ul>
<h4 className="font-semibold">Files:</h4>
<ul className="list-disc list-inside text-sm text-muted-foreground">
{selectedItem.metadata.files.map((file) => (
<li key={file}>{file}</li>
))}
</ul>
</div>
<DialogFooter className="sm:justify-start">
<DialogClose asChild>
<Button type="button" variant="secondary">
Close
</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
)}
</div>
);
}

97
app/settings/page.tsx Normal file
View File

@ -0,0 +1,97 @@
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import Link from "next/link";
export default function SettingsPage() {
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">User Settings</h1>
<div className="space-x-2">
<Link href="/news" passHref>
<Button variant="ghost">News</Button>
</Link>
<Link href="/upgrade" passHref>
<Button variant="ghost">Upgrade</Button>
</Link>
<Link href="/" passHref>
<Button variant="outline">Back to Indexer</Button>
</Link>
</div>
</div>
<Card>
<CardHeader>
<CardTitle>Account Information</CardTitle>
<CardDescription>Manage your account details.</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-1">
<label htmlFor="username" className="text-sm font-medium">
Username
</label>
<Input id="username" defaultValue="CurrentUser" disabled />
</div>
<div className="space-y-1">
<label htmlFor="email" className="text-sm font-medium">
Email
</label>
<Input id="email" type="email" defaultValue="user@example.com" disabled />
</div>
</CardContent>
<CardFooter>
<Button disabled>Update Account (Not Implemented)</Button>
</CardFooter>
</Card>
<Card>
<CardHeader>
<CardTitle>API Key</CardTitle>
<CardDescription>
Manage your API key for external applications.
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-1">
<label htmlFor="apiKey" className="text-sm font-medium">
Your API Key
</label>
<div className="flex items-center space-x-2">
<Input
id="apiKey"
defaultValue="dummy-api-key-12345abcdef"
readOnly
/>
<Button variant="outline" disabled>Copy (Not Implemented)</Button>
</div>
</div>
</CardContent>
<CardFooter>
<Button variant="secondary" disabled>Regenerate API Key (Not Implemented)</Button>
</CardFooter>
</Card>
<Card className="border-destructive">
<CardHeader>
<CardTitle className="text-destructive">Danger Zone</CardTitle>
<CardDescription>
Permanent actions that cannot be undone.
</CardDescription>
</CardHeader>
<CardContent>
<Button variant="destructive" disabled>
Delete Account (Not Implemented)
</Button>
</CardContent>
</Card>
</div>
);
}

108
app/upgrade/page.tsx Normal file
View File

@ -0,0 +1,108 @@
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import Link from "next/link";
// Placeholder SVG for QR Code
const PlaceholderQrCode = () => (
<svg width="128" height="128" viewBox="0 0 100 100" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<rect width="100" height="100" fill="hsl(var(--muted))"/>
<rect x="10" y="10" width="20" height="20" fill="hsl(var(--foreground))"/>
<rect x="40" y="10" width="20" height="20" fill="hsl(var(--foreground))"/>
<rect x="70" y="10" width="20" height="20" fill="hsl(var(--foreground))"/>
<rect x="10" y="40" width="20" height="20" fill="hsl(var(--foreground))"/>
<rect x="70" y="40" width="20" height="20" fill="hsl(var(--foreground))"/>
<rect x="10" y="70" width="20" height="20" fill="hsl(var(--foreground))"/>
<rect x="40" y="70" width="20" height="20" fill="hsl(var(--foreground))"/>
<rect x="70" y="70" width="20" height="20" fill="hsl(var(--foreground))"/>
<rect x="50" y="50" width="10" height="10" fill="hsl(var(--foreground))"/>
</svg>
);
export default function UpgradePage() {
const xmrAddress = "4YOURXMRADDRESSXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
const ltcAddress = "LYOURLTCADDRESSXXXXXXXXXXXXXXXXXXXXXXX";
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="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>
</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.
</p>
</CardContent>
</Card>
{/* 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.
</p>
</CardContent>
</Card>
</div>
</div>
);
}

21
components.json Normal file
View File

@ -0,0 +1,21 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "app/globals.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"iconLibrary": "lucide"
}

57
components/ui/button.tsx Normal file
View File

@ -0,0 +1,57 @@
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
{
variants: {
variant: {
default:
"bg-primary text-primary-foreground shadow hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
outline:
"border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-9 px-4 py-2",
sm: "h-8 rounded-md px-3 text-xs",
lg: "h-10 rounded-md px-8",
icon: "h-9 w-9",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button"
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
)
}
)
Button.displayName = "Button"
export { Button, buttonVariants }

76
components/ui/card.tsx Normal file
View File

@ -0,0 +1,76 @@
import * as React from "react"
import { cn } from "@/lib/utils"
const Card = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn(
"rounded-xl border bg-card text-card-foreground shadow",
className
)}
{...props}
/>
))
Card.displayName = "Card"
const CardHeader = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex flex-col space-y-1.5 p-6", className)}
{...props}
/>
))
CardHeader.displayName = "CardHeader"
const CardTitle = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("font-semibold leading-none tracking-tight", className)}
{...props}
/>
))
CardTitle.displayName = "CardTitle"
const CardDescription = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
))
CardDescription.displayName = "CardDescription"
const CardContent = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
))
CardContent.displayName = "CardContent"
const CardFooter = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex items-center p-6 pt-0", className)}
{...props}
/>
))
CardFooter.displayName = "CardFooter"
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }

262
components/ui/carousel.tsx Normal file
View File

@ -0,0 +1,262 @@
"use client"
import * as React from "react"
import useEmblaCarousel, {
type UseEmblaCarouselType,
} from "embla-carousel-react"
import { ArrowLeft, ArrowRight } from "lucide-react"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
type CarouselApi = UseEmblaCarouselType[1]
type UseCarouselParameters = Parameters<typeof useEmblaCarousel>
type CarouselOptions = UseCarouselParameters[0]
type CarouselPlugin = UseCarouselParameters[1]
type CarouselProps = {
opts?: CarouselOptions
plugins?: CarouselPlugin
orientation?: "horizontal" | "vertical"
setApi?: (api: CarouselApi) => void
}
type CarouselContextProps = {
carouselRef: ReturnType<typeof useEmblaCarousel>[0]
api: ReturnType<typeof useEmblaCarousel>[1]
scrollPrev: () => void
scrollNext: () => void
canScrollPrev: boolean
canScrollNext: boolean
} & CarouselProps
const CarouselContext = React.createContext<CarouselContextProps | null>(null)
function useCarousel() {
const context = React.useContext(CarouselContext)
if (!context) {
throw new Error("useCarousel must be used within a <Carousel />")
}
return context
}
const Carousel = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement> & CarouselProps
>(
(
{
orientation = "horizontal",
opts,
setApi,
plugins,
className,
children,
...props
},
ref
) => {
const [carouselRef, api] = useEmblaCarousel(
{
...opts,
axis: orientation === "horizontal" ? "x" : "y",
},
plugins
)
const [canScrollPrev, setCanScrollPrev] = React.useState(false)
const [canScrollNext, setCanScrollNext] = React.useState(false)
const onSelect = React.useCallback((api: CarouselApi) => {
if (!api) {
return
}
setCanScrollPrev(api.canScrollPrev())
setCanScrollNext(api.canScrollNext())
}, [])
const scrollPrev = React.useCallback(() => {
api?.scrollPrev()
}, [api])
const scrollNext = React.useCallback(() => {
api?.scrollNext()
}, [api])
const handleKeyDown = React.useCallback(
(event: React.KeyboardEvent<HTMLDivElement>) => {
if (event.key === "ArrowLeft") {
event.preventDefault()
scrollPrev()
} else if (event.key === "ArrowRight") {
event.preventDefault()
scrollNext()
}
},
[scrollPrev, scrollNext]
)
React.useEffect(() => {
if (!api || !setApi) {
return
}
setApi(api)
}, [api, setApi])
React.useEffect(() => {
if (!api) {
return
}
onSelect(api)
api.on("reInit", onSelect)
api.on("select", onSelect)
return () => {
api?.off("select", onSelect)
}
}, [api, onSelect])
return (
<CarouselContext.Provider
value={{
carouselRef,
api: api,
opts,
orientation:
orientation || (opts?.axis === "y" ? "vertical" : "horizontal"),
scrollPrev,
scrollNext,
canScrollPrev,
canScrollNext,
}}
>
<div
ref={ref}
onKeyDownCapture={handleKeyDown}
className={cn("relative", className)}
role="region"
aria-roledescription="carousel"
{...props}
>
{children}
</div>
</CarouselContext.Provider>
)
}
)
Carousel.displayName = "Carousel"
const CarouselContent = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => {
const { carouselRef, orientation } = useCarousel()
return (
<div ref={carouselRef} className="overflow-hidden">
<div
ref={ref}
className={cn(
"flex",
orientation === "horizontal" ? "-ml-4" : "-mt-4 flex-col",
className
)}
{...props}
/>
</div>
)
})
CarouselContent.displayName = "CarouselContent"
const CarouselItem = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => {
const { orientation } = useCarousel()
return (
<div
ref={ref}
role="group"
aria-roledescription="slide"
className={cn(
"min-w-0 shrink-0 grow-0 basis-full",
orientation === "horizontal" ? "pl-4" : "pt-4",
className
)}
{...props}
/>
)
})
CarouselItem.displayName = "CarouselItem"
const CarouselPrevious = React.forwardRef<
HTMLButtonElement,
React.ComponentProps<typeof Button>
>(({ className, variant = "outline", size = "icon", ...props }, ref) => {
const { orientation, scrollPrev, canScrollPrev } = useCarousel()
return (
<Button
ref={ref}
variant={variant}
size={size}
className={cn(
"absolute h-8 w-8 rounded-full",
orientation === "horizontal"
? "-left-12 top-1/2 -translate-y-1/2"
: "-top-12 left-1/2 -translate-x-1/2 rotate-90",
className
)}
disabled={!canScrollPrev}
onClick={scrollPrev}
{...props}
>
<ArrowLeft className="h-4 w-4" />
<span className="sr-only">Previous slide</span>
</Button>
)
})
CarouselPrevious.displayName = "CarouselPrevious"
const CarouselNext = React.forwardRef<
HTMLButtonElement,
React.ComponentProps<typeof Button>
>(({ className, variant = "outline", size = "icon", ...props }, ref) => {
const { orientation, scrollNext, canScrollNext } = useCarousel()
return (
<Button
ref={ref}
variant={variant}
size={size}
className={cn(
"absolute h-8 w-8 rounded-full",
orientation === "horizontal"
? "-right-12 top-1/2 -translate-y-1/2"
: "-bottom-12 left-1/2 -translate-x-1/2 rotate-90",
className
)}
disabled={!canScrollNext}
onClick={scrollNext}
{...props}
>
<ArrowRight className="h-4 w-4" />
<span className="sr-only">Next slide</span>
</Button>
)
})
CarouselNext.displayName = "CarouselNext"
export {
type CarouselApi,
Carousel,
CarouselContent,
CarouselItem,
CarouselPrevious,
CarouselNext,
}

122
components/ui/dialog.tsx Normal file
View File

@ -0,0 +1,122 @@
"use client"
import * as React from "react"
import * as DialogPrimitive from "@radix-ui/react-dialog"
import { X } from "lucide-react"
import { cn } from "@/lib/utils"
const Dialog = DialogPrimitive.Root
const DialogTrigger = DialogPrimitive.Trigger
const DialogPortal = DialogPrimitive.Portal
const DialogClose = DialogPrimitive.Close
const DialogOverlay = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Overlay
ref={ref}
className={cn(
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className
)}
{...props}
/>
))
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
const DialogContent = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<DialogPortal>
<DialogOverlay />
<DialogPrimitive.Content
ref={ref}
className={cn(
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
className
)}
{...props}
>
{children}
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
</DialogPrimitive.Content>
</DialogPortal>
))
DialogContent.displayName = DialogPrimitive.Content.displayName
const DialogHeader = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col space-y-1.5 text-center sm:text-left",
className
)}
{...props}
/>
)
DialogHeader.displayName = "DialogHeader"
const DialogFooter = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
className
)}
{...props}
/>
)
DialogFooter.displayName = "DialogFooter"
const DialogTitle = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Title
ref={ref}
className={cn(
"text-lg font-semibold leading-none tracking-tight",
className
)}
{...props}
/>
))
DialogTitle.displayName = DialogPrimitive.Title.displayName
const DialogDescription = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Description
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
))
DialogDescription.displayName = DialogPrimitive.Description.displayName
export {
Dialog,
DialogPortal,
DialogOverlay,
DialogTrigger,
DialogClose,
DialogContent,
DialogHeader,
DialogFooter,
DialogTitle,
DialogDescription,
}

22
components/ui/input.tsx Normal file
View File

@ -0,0 +1,22 @@
import * as React from "react"
import { cn } from "@/lib/utils"
const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
({ className, type, ...props }, ref) => {
return (
<input
type={type}
className={cn(
"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
className
)}
ref={ref}
{...props}
/>
)
}
)
Input.displayName = "Input"
export { Input }

120
components/ui/table.tsx Normal file
View File

@ -0,0 +1,120 @@
import * as React from "react"
import { cn } from "@/lib/utils"
const Table = React.forwardRef<
HTMLTableElement,
React.HTMLAttributes<HTMLTableElement>
>(({ className, ...props }, ref) => (
<div className="relative w-full overflow-auto">
<table
ref={ref}
className={cn("w-full caption-bottom text-sm", className)}
{...props}
/>
</div>
))
Table.displayName = "Table"
const TableHeader = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
))
TableHeader.displayName = "TableHeader"
const TableBody = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tbody
ref={ref}
className={cn("[&_tr:last-child]:border-0", className)}
{...props}
/>
))
TableBody.displayName = "TableBody"
const TableFooter = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tfoot
ref={ref}
className={cn(
"border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
className
)}
{...props}
/>
))
TableFooter.displayName = "TableFooter"
const TableRow = React.forwardRef<
HTMLTableRowElement,
React.HTMLAttributes<HTMLTableRowElement>
>(({ className, ...props }, ref) => (
<tr
ref={ref}
className={cn(
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
className
)}
{...props}
/>
))
TableRow.displayName = "TableRow"
const TableHead = React.forwardRef<
HTMLTableCellElement,
React.ThHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<th
ref={ref}
className={cn(
"h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
className
)}
{...props}
/>
))
TableHead.displayName = "TableHead"
const TableCell = React.forwardRef<
HTMLTableCellElement,
React.TdHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<td
ref={ref}
className={cn(
"p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
className
)}
{...props}
/>
))
TableCell.displayName = "TableCell"
const TableCaption = React.forwardRef<
HTMLTableCaptionElement,
React.HTMLAttributes<HTMLTableCaptionElement>
>(({ className, ...props }, ref) => (
<caption
ref={ref}
className={cn("mt-4 text-sm text-muted-foreground", className)}
{...props}
/>
))
TableCaption.displayName = "TableCaption"
export {
Table,
TableHeader,
TableBody,
TableFooter,
TableHead,
TableRow,
TableCell,
TableCaption,
}

55
components/ui/tabs.tsx Normal file
View File

@ -0,0 +1,55 @@
"use client"
import * as React from "react"
import * as TabsPrimitive from "@radix-ui/react-tabs"
import { cn } from "@/lib/utils"
const Tabs = TabsPrimitive.Root
const TabsList = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.List>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
>(({ className, ...props }, ref) => (
<TabsPrimitive.List
ref={ref}
className={cn(
"inline-flex h-9 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground",
className
)}
{...props}
/>
))
TabsList.displayName = TabsPrimitive.List.displayName
const TabsTrigger = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
>(({ className, ...props }, ref) => (
<TabsPrimitive.Trigger
ref={ref}
className={cn(
"inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow",
className
)}
{...props}
/>
))
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
const TabsContent = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
>(({ className, ...props }, ref) => (
<TabsPrimitive.Content
ref={ref}
className={cn(
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
className
)}
{...props}
/>
))
TabsContent.displayName = TabsPrimitive.Content.displayName
export { Tabs, TabsList, TabsTrigger, TabsContent }

6
lib/utils.ts Normal file
View File

@ -0,0 +1,6 @@
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}

786
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -9,19 +9,29 @@
"lint": "next lint"
},
"dependencies": {
"@radix-ui/react-dialog": "^1.1.14",
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-tabs": "^1.1.12",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"embla-carousel-autoplay": "^8.6.0",
"embla-carousel-react": "^8.6.0",
"lucide-react": "^0.511.0",
"next": "15.1.8",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"next": "15.1.8"
"tailwind-merge": "^3.3.0",
"tailwindcss-animate": "^1.0.7"
},
"devDependencies": {
"typescript": "^5",
"@eslint/eslintrc": "^3",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"postcss": "^8",
"tailwindcss": "^3.4.1",
"eslint": "^9",
"eslint-config-next": "15.1.8",
"@eslint/eslintrc": "^3"
"postcss": "^8",
"tailwindcss": "^3.4.1",
"typescript": "^5"
}
}

View File

@ -1,18 +1,62 @@
import type { Config } from "tailwindcss";
export default {
content: [
darkMode: ["class"],
content: [
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
colors: {
background: "var(--background)",
foreground: "var(--foreground)",
},
},
extend: {
colors: {
background: 'hsl(var(--background))',
foreground: 'hsl(var(--foreground))',
card: {
DEFAULT: 'hsl(var(--card))',
foreground: 'hsl(var(--card-foreground))'
},
popover: {
DEFAULT: 'hsl(var(--popover))',
foreground: 'hsl(var(--popover-foreground))'
},
primary: {
DEFAULT: 'hsl(var(--primary))',
foreground: 'hsl(var(--primary-foreground))'
},
secondary: {
DEFAULT: 'hsl(var(--secondary))',
foreground: 'hsl(var(--secondary-foreground))'
},
muted: {
DEFAULT: 'hsl(var(--muted))',
foreground: 'hsl(var(--muted-foreground))'
},
accent: {
DEFAULT: 'hsl(var(--accent))',
foreground: 'hsl(var(--accent-foreground))'
},
destructive: {
DEFAULT: 'hsl(var(--destructive))',
foreground: 'hsl(var(--destructive-foreground))'
},
border: 'hsl(var(--border))',
input: 'hsl(var(--input))',
ring: 'hsl(var(--ring))',
chart: {
'1': 'hsl(var(--chart-1))',
'2': 'hsl(var(--chart-2))',
'3': 'hsl(var(--chart-3))',
'4': 'hsl(var(--chart-4))',
'5': 'hsl(var(--chart-5))'
}
},
borderRadius: {
lg: 'var(--radius)',
md: 'calc(var(--radius) - 2px)',
sm: 'calc(var(--radius) - 4px)'
}
}
},
plugins: [],
plugins: [require("tailwindcss-animate")],
} satisfies Config;