feat(theme): barnyard redesign with rustic crypto aesthetic
- New color palette: barn red primary, cream backgrounds, hay gold - Added Abril Fatface display font for headlines - Wood grain texture backgrounds via CSS gradients - Barnyard personality in copy: 'Join the Flock', 'Enter the Coop' - Replaced hardcoded slate colors with theme variables - Updated landing, login, dashboard, onboarding, leaderboard
This commit is contained in:
@@ -49,7 +49,7 @@ export default function AuthCallbackPage() {
|
||||
}, [router, setUser, setToken]);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-slate-950 p-4">
|
||||
<div className="min-h-screen flex items-center justify-center bg-background p-4">
|
||||
<div className="text-center">
|
||||
<Loader2 className="mx-auto h-12 w-12 animate-spin text-primary mb-4" />
|
||||
<p className="text-lg text-foreground">{status}</p>
|
||||
|
||||
@@ -1,114 +1,144 @@
|
||||
'use client';
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { userApi } from '@/lib/api';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import { formatNumber, formatDuration } from '@/lib/utils';
|
||||
import { Trophy, Medal, Star, Clock, Coins } from 'lucide-react';
|
||||
import { Clock, Coins, Medal, Star, Trophy } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { userApi } from "@/lib/api";
|
||||
import { formatDuration, formatNumber } from "@/lib/utils";
|
||||
|
||||
interface LeaderboardUser {
|
||||
id: string;
|
||||
plexUsername: string;
|
||||
totalEarned: number;
|
||||
watchTimeMinutes: number;
|
||||
id: string;
|
||||
plexUsername: string;
|
||||
totalEarned: number;
|
||||
watchTimeMinutes: number;
|
||||
}
|
||||
|
||||
export function Leaderboard() {
|
||||
const [data, setData] = useState<{ topEarners: LeaderboardUser[]; topWatchers: LeaderboardUser[] }>({
|
||||
topEarners: [],
|
||||
topWatchers: [],
|
||||
});
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [data, setData] = useState<{
|
||||
topEarners: LeaderboardUser[];
|
||||
topWatchers: LeaderboardUser[];
|
||||
}>({
|
||||
topEarners: [],
|
||||
topWatchers: [],
|
||||
});
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
loadLeaderboard();
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
loadLeaderboard();
|
||||
}, []);
|
||||
|
||||
const loadLeaderboard = async () => {
|
||||
try {
|
||||
const response = await userApi.getLeaderboard();
|
||||
setData(response.data);
|
||||
} catch (error) {
|
||||
console.error('Failed to load leaderboard:', error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
const loadLeaderboard = async () => {
|
||||
try {
|
||||
const response = await userApi.getLeaderboard();
|
||||
setData(response.data);
|
||||
} catch (error) {
|
||||
console.error("Failed to load leaderboard:", error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const getRankIcon = (index: number) => {
|
||||
switch (index) {
|
||||
case 0: return <Trophy className="h-4 w-4 text-yellow-500" />;
|
||||
case 1: return <Medal className="h-4 w-4 text-slate-400" />;
|
||||
case 2: return <Medal className="h-4 w-4 text-amber-600" />;
|
||||
default: return <span className="w-4 text-center text-xs text-muted-foreground">{index + 1}</span>;
|
||||
}
|
||||
};
|
||||
const getRankIcon = (index: number) => {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return <Trophy className="h-4 w-4 text-yellow-500" />;
|
||||
case 1:
|
||||
return <Medal className="h-4 w-4 text-muted-foreground" />;
|
||||
case 2:
|
||||
return <Medal className="h-4 w-4 text-amber-600" />;
|
||||
default:
|
||||
return (
|
||||
<span className="w-4 text-center text-xs text-muted-foreground">
|
||||
{index + 1}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<Card>
|
||||
<CardContent className="py-8 text-center text-muted-foreground">
|
||||
<div className="animate-pulse space-y-4">
|
||||
{[1, 2, 3, 4, 5].map((i) => (
|
||||
<div key={i} className="h-10 bg-muted rounded-md" />
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
if (isLoading) {
|
||||
return (
|
||||
<Card>
|
||||
<CardContent className="py-8 text-center text-muted-foreground">
|
||||
<div className="animate-pulse space-y-4">
|
||||
{[1, 2, 3, 4, 5].map((i) => (
|
||||
<div key={i} className="h-10 bg-muted rounded-md" />
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Card className="border-none bg-muted/30 shadow-none">
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-lg font-bold flex items-center gap-2">
|
||||
<Trophy className="h-5 w-5 text-yellow-500" />
|
||||
Leaderboard
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Tabs defaultValue="earners" className="w-full">
|
||||
<TabsList className="grid w-full grid-cols-2 mb-4 h-8 bg-background/50">
|
||||
<TabsTrigger value="earners" className="text-xs py-1">Top Earners</TabsTrigger>
|
||||
<TabsTrigger value="watchers" className="text-xs py-1">Top Watchers</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="earners" className="mt-0">
|
||||
<div className="space-y-2">
|
||||
{data.topEarners.map((user, index) => (
|
||||
<div key={user.id} className="flex items-center justify-between p-2 rounded-lg bg-background/40 hover:bg-background/60 transition-colors">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-6 flex justify-center">{getRankIcon(index)}</div>
|
||||
<span className="text-sm font-medium">{user.plexUsername}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1 text-xs font-bold text-green-500">
|
||||
<Coins className="h-3 w-3" />
|
||||
{formatNumber(user.totalEarned)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="watchers" className="mt-0">
|
||||
<div className="space-y-2">
|
||||
{data.topWatchers.map((user, index) => (
|
||||
<div key={user.id} className="flex items-center justify-between p-2 rounded-lg bg-background/40 hover:bg-background/60 transition-colors">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-6 flex justify-center">{getRankIcon(index)}</div>
|
||||
<span className="text-sm font-medium">{user.plexUsername}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1 text-xs font-medium text-muted-foreground">
|
||||
<Clock className="h-3 w-3" />
|
||||
{Math.floor(user.watchTimeMinutes / 60)}h {user.watchTimeMinutes % 60}m
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
return (
|
||||
<Card className="border-none bg-muted/30 shadow-none">
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-lg font-bold flex items-center gap-2">
|
||||
<Trophy className="h-5 w-5 text-yellow-500" />
|
||||
Leaderboard
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Tabs defaultValue="earners" className="w-full">
|
||||
<TabsList className="grid w-full grid-cols-2 mb-4 h-8 bg-background/50">
|
||||
<TabsTrigger value="earners" className="text-xs py-1">
|
||||
Top Earners
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="watchers" className="text-xs py-1">
|
||||
Top Watchers
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="earners" className="mt-0">
|
||||
<div className="space-y-2">
|
||||
{data.topEarners.map((user, index) => (
|
||||
<div
|
||||
key={user.id}
|
||||
className="flex items-center justify-between p-2 rounded-lg bg-background/40 hover:bg-background/60 transition-colors"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-6 flex justify-center">
|
||||
{getRankIcon(index)}
|
||||
</div>
|
||||
<span className="text-sm font-medium">
|
||||
{user.plexUsername}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1 text-xs font-bold text-green-500">
|
||||
<Coins className="h-3 w-3" />
|
||||
{formatNumber(user.totalEarned)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="watchers" className="mt-0">
|
||||
<div className="space-y-2">
|
||||
{data.topWatchers.map((user, index) => (
|
||||
<div
|
||||
key={user.id}
|
||||
className="flex items-center justify-between p-2 rounded-lg bg-background/40 hover:bg-background/60 transition-colors"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-6 flex justify-center">
|
||||
{getRankIcon(index)}
|
||||
</div>
|
||||
<span className="text-sm font-medium">
|
||||
{user.plexUsername}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1 text-xs font-medium text-muted-foreground">
|
||||
<Clock className="h-3 w-3" />
|
||||
{Math.floor(user.watchTimeMinutes / 60)}h{" "}
|
||||
{user.watchTimeMinutes % 60}m
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,92 +1,116 @@
|
||||
'use client';
|
||||
"use client";
|
||||
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Wallet, Sparkles, ShieldCheck, Zap, ExternalLink } from 'lucide-react';
|
||||
import { useWallet } from '@solana/wallet-adapter-react';
|
||||
import { WalletMultiButton } from '@solana/wallet-adapter-react-ui';
|
||||
import { useEffect } from 'react';
|
||||
import { walletApi } from '@/lib/api';
|
||||
import { toast } from 'sonner';
|
||||
import { useWallet } from "@solana/wallet-adapter-react";
|
||||
import { WalletMultiButton } from "@solana/wallet-adapter-react-ui";
|
||||
import {
|
||||
Egg,
|
||||
ExternalLink,
|
||||
ShieldCheck,
|
||||
Sparkles,
|
||||
Wallet,
|
||||
Zap,
|
||||
} from "lucide-react";
|
||||
import { useEffect } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { walletApi } from "@/lib/api";
|
||||
|
||||
interface WelcomeOnboardingProps {
|
||||
onStart: () => void;
|
||||
onConnected: () => void;
|
||||
onStart: () => void;
|
||||
onConnected: () => void;
|
||||
}
|
||||
|
||||
export function WelcomeOnboarding({ onStart, onConnected }: WelcomeOnboardingProps) {
|
||||
const { publicKey, connected } = useWallet();
|
||||
export function WelcomeOnboarding({
|
||||
onStart,
|
||||
onConnected,
|
||||
}: WelcomeOnboardingProps) {
|
||||
const { publicKey, connected } = useWallet();
|
||||
|
||||
useEffect(() => {
|
||||
if (connected && publicKey) {
|
||||
handleConnectWallet(publicKey.toString());
|
||||
}
|
||||
}, [connected, publicKey]);
|
||||
useEffect(() => {
|
||||
if (connected && publicKey) {
|
||||
handleConnectWallet(publicKey.toString());
|
||||
}
|
||||
}, [connected, publicKey]);
|
||||
|
||||
const handleConnectWallet = async (address: string) => {
|
||||
try {
|
||||
await walletApi.connectWallet(address);
|
||||
toast.success('Wallet connected to your account!');
|
||||
onConnected();
|
||||
} catch (error) {
|
||||
toast.error('Failed to link wallet');
|
||||
}
|
||||
};
|
||||
const handleConnectWallet = async (address: string) => {
|
||||
try {
|
||||
await walletApi.connectWallet(address);
|
||||
toast.success("Wallet connected to your account!");
|
||||
onConnected();
|
||||
} catch (error) {
|
||||
toast.error("Failed to link wallet");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Card className="mb-8 overflow-hidden border-primary/20 bg-gradient-to-br from-primary/5 via-background to-background">
|
||||
<CardContent className="p-0">
|
||||
<div className="flex flex-col md:flex-row">
|
||||
<div className="flex-1 p-8 space-y-6">
|
||||
<div className="space-y-2">
|
||||
<h2 className="text-3xl font-bold tracking-tight">Welcome to the Ecosystem!</h2>
|
||||
<p className="text-muted-foreground text-lg">
|
||||
You're just one step away from earning rewards for your watch time.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-6">
|
||||
<div className="flex gap-3">
|
||||
<div className="mt-1 bg-primary/10 p-2 rounded-lg h-fit">
|
||||
<Zap className="h-4 w-4 text-primary" />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-semibold">Automatic Rewards</h4>
|
||||
<p className="text-sm text-muted-foreground">Credits are minted directly to your wallet while you watch.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-3">
|
||||
<div className="mt-1 bg-primary/10 p-2 rounded-lg h-fit">
|
||||
<ShieldCheck className="h-4 w-4 text-primary" />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-semibold">Secure & Private</h4>
|
||||
<p className="text-sm text-muted-foreground">Your wallet is personal and secured on the Solana blockchain.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
return (
|
||||
<Card className="mb-8 overflow-hidden border-2 border-primary/20 bg-gradient-to-br from-primary/5 via-background to-background">
|
||||
<CardContent className="p-0">
|
||||
<div className="flex flex-col md:flex-row">
|
||||
<div className="flex-1 p-8 space-y-6">
|
||||
<div className="space-y-2">
|
||||
<h2 className="font-display text-3xl tracking-tight">
|
||||
Welcome to the Coop!
|
||||
</h2>
|
||||
<p className="text-muted-foreground text-lg">
|
||||
You are one step away from earning golden eggs for your watch
|
||||
time.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-4 pt-2">
|
||||
<Button size="lg" onClick={onStart} className="h-12 px-8 rounded-full shadow-lg shadow-primary/20">
|
||||
<Wallet className="mr-2 h-5 w-5" />
|
||||
Create Managed Wallet
|
||||
</Button>
|
||||
|
||||
<div className="wallet-adapter-custom-wrapper">
|
||||
<WalletMultiButton className="h-12 !rounded-full !bg-secondary !text-secondary-foreground hover:!bg-secondary/80" />
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground italic">
|
||||
Choose "Create Managed Wallet" for an easy start, or "Select Wallet" to use your own (Phantom, Solflare, etc.)
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="hidden md:flex flex-none w-72 bg-primary/10 items-center justify-center border-l border-primary/10">
|
||||
<Sparkles className="h-32 w-32 text-primary opacity-20 animate-pulse" />
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-6">
|
||||
<div className="flex gap-3">
|
||||
<div className="mt-1 bg-primary/10 p-2 rounded-lg h-fit">
|
||||
<Zap className="h-4 w-4 text-primary" />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-semibold">Automatic Rewards</h4>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Credits are minted directly to your wallet while you watch.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-3">
|
||||
<div className="mt-1 bg-primary/10 p-2 rounded-lg h-fit">
|
||||
<ShieldCheck className="h-4 w-4 text-primary" />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-semibold">Secure & Private</h4>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Your wallet is personal and secured on the Solana
|
||||
blockchain.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-4 pt-2">
|
||||
<Button
|
||||
size="lg"
|
||||
onClick={onStart}
|
||||
className="h-12 px-8 rounded-full shadow-lg shadow-primary/20"
|
||||
>
|
||||
<Wallet className="mr-2 h-5 w-5" />
|
||||
Create Managed Wallet
|
||||
</Button>
|
||||
|
||||
<div className="wallet-adapter-custom-wrapper">
|
||||
<WalletMultiButton className="h-12 !rounded-full !bg-secondary !text-secondary-foreground hover:!bg-secondary/80" />
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground italic">
|
||||
Choose “Create Managed Wallet” for an easy start, or
|
||||
“Select Wallet” to use your own (Phantom, Solflare,
|
||||
etc.)
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="hidden md:flex flex-none w-72 bg-primary/10 items-center justify-center border-l border-primary/10">
|
||||
<Egg className="h-32 w-32 text-primary opacity-20 animate-pulse" />
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
+296
-261
@@ -1,285 +1,320 @@
|
||||
'use client';
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useStore } from '@/lib/store';
|
||||
import { walletApi, transactionApi, overseerApi } from '@/lib/api';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import {
|
||||
Wallet,
|
||||
TrendingUp,
|
||||
TrendingDown,
|
||||
Clock,
|
||||
Film,
|
||||
ExternalLink,
|
||||
Plus,
|
||||
History
|
||||
} from 'lucide-react';
|
||||
import { formatNumber, truncateAddress } from '@/lib/utils';
|
||||
import { toast } from 'sonner';
|
||||
import { useSocket } from '@/lib/socket';
|
||||
import { WalletMultiButton } from '@solana/wallet-adapter-react-ui';
|
||||
import { TransactionList } from './components/TransactionList';
|
||||
import { WatchHistory } from './components/WatchHistory';
|
||||
import { CreateWalletModal } from './components/CreateWalletModal';
|
||||
import { WelcomeOnboarding } from './components/WelcomeOnboarding';
|
||||
import { ActivityFeed } from './components/ActivityFeed';
|
||||
import { SearchRequestModal } from './components/SearchRequestModal';
|
||||
import { RequestHistory } from './components/RequestHistory';
|
||||
import { Leaderboard } from './components/Leaderboard';
|
||||
import { WalletMultiButton } from "@solana/wallet-adapter-react-ui";
|
||||
import {
|
||||
Clock,
|
||||
Egg,
|
||||
ExternalLink,
|
||||
Film,
|
||||
History,
|
||||
TrendingDown,
|
||||
TrendingUp,
|
||||
Wallet,
|
||||
} from "lucide-react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useEffect, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { overseerApi, transactionApi, walletApi } from "@/lib/api";
|
||||
import { useSocket } from "@/lib/socket";
|
||||
import { useStore } from "@/lib/store";
|
||||
import { formatNumber, truncateAddress } from "@/lib/utils";
|
||||
import { ActivityFeed } from "./components/ActivityFeed";
|
||||
import { CreateWalletModal } from "./components/CreateWalletModal";
|
||||
import { Leaderboard } from "./components/Leaderboard";
|
||||
import { RequestHistory } from "./components/RequestHistory";
|
||||
import { SearchRequestModal } from "./components/SearchRequestModal";
|
||||
import { TransactionList } from "./components/TransactionList";
|
||||
import { WatchHistory } from "./components/WatchHistory";
|
||||
import { WelcomeOnboarding } from "./components/WelcomeOnboarding";
|
||||
|
||||
interface WalletData {
|
||||
hasWallet: boolean;
|
||||
address?: string;
|
||||
balance: number;
|
||||
totalEarned: number;
|
||||
totalSpent: number;
|
||||
explorerUrl?: string;
|
||||
hasWallet: boolean;
|
||||
address?: string;
|
||||
balance: number;
|
||||
totalEarned: number;
|
||||
totalSpent: number;
|
||||
explorerUrl?: string;
|
||||
}
|
||||
|
||||
export default function DashboardPage() {
|
||||
const router = useRouter();
|
||||
const { user, isAuthenticated, logout } = useStore();
|
||||
const [wallet, setWallet] = useState<WalletData | null>(null);
|
||||
const [requestCosts, setRequestCosts] = useState({ movie: 100, tv: 200 });
|
||||
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
|
||||
const [isSearchModalOpen, setIsSearchModalOpen] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const socket = useSocket();
|
||||
const router = useRouter();
|
||||
const { user, isAuthenticated, logout } = useStore();
|
||||
const [wallet, setWallet] = useState<WalletData | null>(null);
|
||||
const [requestCosts, setRequestCosts] = useState({ movie: 100, tv: 200 });
|
||||
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
|
||||
const [isSearchModalOpen, setIsSearchModalOpen] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const socket = useSocket();
|
||||
|
||||
useEffect(() => {
|
||||
if (!isAuthenticated) {
|
||||
router.push('/login');
|
||||
return;
|
||||
}
|
||||
useEffect(() => {
|
||||
if (!isAuthenticated) {
|
||||
router.push("/login");
|
||||
return;
|
||||
}
|
||||
|
||||
loadData();
|
||||
}, [isAuthenticated, router]);
|
||||
loadData();
|
||||
}, [isAuthenticated, router]);
|
||||
|
||||
useEffect(() => {
|
||||
if (socket) {
|
||||
socket.on('credits_earned', (data) => {
|
||||
toast.success(`You earned ${data.amount} $COOP!`, {
|
||||
description: `Watched: ${data.title}`,
|
||||
});
|
||||
loadData();
|
||||
});
|
||||
useEffect(() => {
|
||||
if (socket) {
|
||||
socket.on("credits_earned", (data) => {
|
||||
toast.success(`You earned ${data.amount} $COOP!`, {
|
||||
description: `Watched: ${data.title}`,
|
||||
});
|
||||
loadData();
|
||||
});
|
||||
|
||||
socket.on('bonus_received', (data) => {
|
||||
toast.success(`Bonus Received: ${data.amount} $COOP!`, {
|
||||
description: data.reason,
|
||||
});
|
||||
loadData();
|
||||
});
|
||||
socket.on("bonus_received", (data) => {
|
||||
toast.success(`Bonus Received: ${data.amount} $COOP!`, {
|
||||
description: data.reason,
|
||||
});
|
||||
loadData();
|
||||
});
|
||||
|
||||
socket.on('credits_spent', (data) => {
|
||||
toast.info(`Requested: ${data.title}`, {
|
||||
description: `Spent ${data.amount} $COOP`,
|
||||
});
|
||||
loadData();
|
||||
});
|
||||
socket.on("credits_spent", (data) => {
|
||||
toast.info(`Requested: ${data.title}`, {
|
||||
description: `Spent ${data.amount} $COOP`,
|
||||
});
|
||||
loadData();
|
||||
});
|
||||
|
||||
return () => {
|
||||
socket.off('credits_earned');
|
||||
socket.off('bonus_received');
|
||||
socket.off('credits_spent');
|
||||
};
|
||||
}
|
||||
}, [socket]);
|
||||
return () => {
|
||||
socket.off("credits_earned");
|
||||
socket.off("bonus_received");
|
||||
socket.off("credits_spent");
|
||||
};
|
||||
}
|
||||
}, [socket]);
|
||||
|
||||
const loadData = async () => {
|
||||
try {
|
||||
const [walletRes, costsRes] = await Promise.all([
|
||||
walletApi.getWallet(),
|
||||
overseerApi.getCosts().catch(() => ({ data: { movie: 100, tv: 200 } }))
|
||||
]);
|
||||
|
||||
setWallet(walletRes.data);
|
||||
setRequestCosts(costsRes.data);
|
||||
} catch (error) {
|
||||
toast.error('Failed to load wallet data');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
const loadData = async () => {
|
||||
try {
|
||||
const [walletRes, costsRes] = await Promise.all([
|
||||
walletApi.getWallet(),
|
||||
overseerApi.getCosts().catch(() => ({ data: { movie: 100, tv: 200 } })),
|
||||
]);
|
||||
|
||||
if (!isAuthenticated || !user) {
|
||||
return null;
|
||||
}
|
||||
setWallet(walletRes.data);
|
||||
setRequestCosts(costsRes.data);
|
||||
} catch (error) {
|
||||
toast.error("Failed to load wallet data");
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
{/* Header */}
|
||||
<header className="border-b bg-card">
|
||||
<div className="max-w-7xl mx-auto px-4 py-4 flex items-center justify-between">
|
||||
<div className="flex items-center gap-4">
|
||||
<h1 className="text-2xl font-bold">CoopCredits</h1>
|
||||
<Badge variant="secondary">{user.plexUsername}</Badge>
|
||||
{user.isAdmin && (
|
||||
<Button variant="outline" size="sm" onClick={() => router.push('/admin')}>
|
||||
Admin
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="wallet-adapter-custom-wrapper hidden md:block">
|
||||
<WalletMultiButton className="!h-9 !px-4 !text-sm !rounded-md !bg-secondary !text-secondary-foreground hover:!bg-secondary/80" />
|
||||
</div>
|
||||
<Button variant="ghost" onClick={logout}>
|
||||
Sign Out
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
if (!isAuthenticated || !user) {
|
||||
return null;
|
||||
}
|
||||
|
||||
<main className="max-w-7xl mx-auto px-4 py-8">
|
||||
{/* Onboarding for new users */}
|
||||
{!wallet?.hasWallet && !isLoading && (
|
||||
<WelcomeOnboarding onStart={() => setIsCreateModalOpen(true)} onConnected={loadData} />
|
||||
)}
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
{/* Header */}
|
||||
<header className="border-b-2 bg-card">
|
||||
<div className="max-w-7xl mx-auto px-4 py-4 flex items-center justify-between">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Egg className="w-6 h-6 text-primary" />
|
||||
<h1 className="font-display text-2xl">The Coop</h1>
|
||||
</div>
|
||||
<Badge variant="secondary">{user.plexUsername}</Badge>
|
||||
{user.isAdmin && (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => router.push("/admin")}
|
||||
>
|
||||
Admin
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="wallet-adapter-custom-wrapper hidden md:block">
|
||||
<WalletMultiButton className="!h-9 !px-4 !text-sm !rounded-md !bg-secondary !text-secondary-foreground hover:!bg-secondary/80" />
|
||||
</div>
|
||||
<Button variant="ghost" onClick={logout}>
|
||||
Sign Out
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Stats Cards */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-8">
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center justify-between pb-2">
|
||||
<CardTitle className="text-sm font-medium">Balance</CardTitle>
|
||||
<Wallet className="h-4 w-4 text-muted-foreground" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold">
|
||||
{wallet ? formatNumber(wallet.balance) : '--'} $COOP
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{wallet?.hasWallet ? 'Ready to spend' : 'Create wallet to start'}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<main className="max-w-7xl mx-auto px-4 py-8">
|
||||
{/* Onboarding for new users */}
|
||||
{!wallet?.hasWallet && !isLoading && (
|
||||
<WelcomeOnboarding
|
||||
onStart={() => setIsCreateModalOpen(true)}
|
||||
onConnected={loadData}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center justify-between pb-2">
|
||||
<CardTitle className="text-sm font-medium">Total Earned</CardTitle>
|
||||
<TrendingUp className="h-4 w-4 text-green-500" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold">
|
||||
{wallet ? formatNumber(wallet.totalEarned) : '--'} $COOP
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
From watching content
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
{/* Stats Cards */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-8">
|
||||
<Card className="border-2">
|
||||
<CardHeader className="flex flex-row items-center justify-between pb-2">
|
||||
<CardTitle className="text-sm font-medium">Nest Egg</CardTitle>
|
||||
<Wallet className="h-4 w-4 text-muted-foreground" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold">
|
||||
{wallet ? formatNumber(wallet.balance) : "--"} $COOP
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{wallet?.hasWallet
|
||||
? "Ready to spend"
|
||||
: "Create wallet to start"}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center justify-between pb-2">
|
||||
<CardTitle className="text-sm font-medium">Total Spent</CardTitle>
|
||||
<TrendingDown className="h-4 w-4 text-red-500" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold">
|
||||
{wallet ? formatNumber(wallet.totalSpent) : '--'} $COOP
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
On content requests
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card className="border-2">
|
||||
<CardHeader className="flex flex-row items-center justify-between pb-2">
|
||||
<CardTitle className="text-sm font-medium">
|
||||
Total Gathered
|
||||
</CardTitle>
|
||||
<TrendingUp className="h-4 w-4 text-green-500" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold">
|
||||
{wallet ? formatNumber(wallet.totalEarned) : "--"} $COOP
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
From watching content
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="cursor-pointer hover:border-primary/50 transition-colors group" onClick={() => setIsSearchModalOpen(true)}>
|
||||
<CardHeader className="flex flex-row items-center justify-between pb-2">
|
||||
<CardTitle className="text-sm font-medium">Request Cost</CardTitle>
|
||||
<Film className="h-4 w-4 text-muted-foreground group-hover:text-primary transition-colors" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold">{requestCosts.movie} $COOP</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Click to request content
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
<Card className="border-2">
|
||||
<CardHeader className="flex flex-row items-center justify-between pb-2">
|
||||
<CardTitle className="text-sm font-medium">Total Spent</CardTitle>
|
||||
<TrendingDown className="h-4 w-4 text-red-500" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold">
|
||||
{wallet ? formatNumber(wallet.totalSpent) : "--"} $COOP
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
On content requests
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Wallet Section removed from here if redundant, or kept if we want it to show after creation */}
|
||||
{wallet?.hasWallet && (
|
||||
<Card className="mb-8">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<Wallet className="h-5 w-5" />
|
||||
Your Wallet
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
Manage your Solana wallet and $COOP tokens
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="flex items-center justify-between p-4 bg-muted rounded-lg">
|
||||
<div>
|
||||
<p className="text-sm text-muted-foreground">Address</p>
|
||||
<p className="font-mono font-medium">
|
||||
{truncateAddress(wallet.address!)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Button variant="outline" size="sm" onClick={() => setIsCreateModalOpen(true)}>
|
||||
<History className="mr-2 h-4 w-4" />
|
||||
Backup
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" asChild>
|
||||
<a href={wallet.explorerUrl} target="_blank" rel="noopener noreferrer">
|
||||
<ExternalLink className="mr-2 h-4 w-4" />
|
||||
Explorer
|
||||
</a>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
<Card
|
||||
className="border-2 cursor-pointer hover:border-primary/50 transition-colors group"
|
||||
onClick={() => setIsSearchModalOpen(true)}
|
||||
>
|
||||
<CardHeader className="flex flex-row items-center justify-between pb-2">
|
||||
<CardTitle className="text-sm font-medium">
|
||||
Request Cost
|
||||
</CardTitle>
|
||||
<Film className="h-4 w-4 text-muted-foreground group-hover:text-primary transition-colors" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold">
|
||||
{requestCosts.movie} $COOP
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Click to request content
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Tabs */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||
<div className="lg:col-span-2">
|
||||
<Tabs defaultValue="transactions" className="space-y-4">
|
||||
<TabsList>
|
||||
<TabsTrigger value="transactions">Transactions</TabsTrigger>
|
||||
<TabsTrigger value="history">Watch History</TabsTrigger>
|
||||
<TabsTrigger value="requests">My Requests</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="transactions">
|
||||
<TransactionList />
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="history">
|
||||
<WatchHistory />
|
||||
</TabsContent>
|
||||
<TabsContent value="requests">
|
||||
<RequestHistory />
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
<div className="lg:col-span-1 space-y-8">
|
||||
<ActivityFeed />
|
||||
<Leaderboard />
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
{/* Wallet Section */}
|
||||
{wallet?.hasWallet && (
|
||||
<Card className="mb-8 border-2">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<Wallet className="h-5 w-5" />
|
||||
Your Coop Wallet
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
Manage your Solana wallet and $COOP tokens
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="flex items-center justify-between p-4 bg-muted rounded-lg">
|
||||
<div>
|
||||
<p className="text-sm text-muted-foreground">Address</p>
|
||||
<p className="font-mono font-medium">
|
||||
{truncateAddress(wallet.address!)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => setIsCreateModalOpen(true)}
|
||||
>
|
||||
<History className="mr-2 h-4 w-4" />
|
||||
Backup
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" asChild>
|
||||
<a
|
||||
href={wallet.explorerUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<ExternalLink className="mr-2 h-4 w-4" />
|
||||
Explorer
|
||||
</a>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
<CreateWalletModal
|
||||
open={isCreateModalOpen}
|
||||
onClose={() => setIsCreateModalOpen(false)}
|
||||
onCreated={loadData}
|
||||
/>
|
||||
<SearchRequestModal
|
||||
open={isSearchModalOpen}
|
||||
onClose={() => setIsSearchModalOpen(false)}
|
||||
onRequested={loadData}
|
||||
costs={requestCosts}
|
||||
balance={wallet?.balance || 0}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
{/* Tabs */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||
<div className="lg:col-span-2">
|
||||
<Tabs defaultValue="transactions" className="space-y-4">
|
||||
<TabsList className="border-2">
|
||||
<TabsTrigger value="transactions">Transactions</TabsTrigger>
|
||||
<TabsTrigger value="history">Watch History</TabsTrigger>
|
||||
<TabsTrigger value="requests">My Requests</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="transactions">
|
||||
<TransactionList />
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="history">
|
||||
<WatchHistory />
|
||||
</TabsContent>
|
||||
<TabsContent value="requests">
|
||||
<RequestHistory />
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
<div className="lg:col-span-1 space-y-8">
|
||||
<ActivityFeed />
|
||||
<Leaderboard />
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<CreateWalletModal
|
||||
open={isCreateModalOpen}
|
||||
onClose={() => setIsCreateModalOpen(false)}
|
||||
onCreated={loadData}
|
||||
/>
|
||||
<SearchRequestModal
|
||||
open={isSearchModalOpen}
|
||||
onClose={() => setIsSearchModalOpen(false)}
|
||||
onRequested={loadData}
|
||||
costs={requestCosts}
|
||||
balance={wallet?.balance || 0}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
@theme {
|
||||
--font-sans: var(--font-inter), ui-sans-serif, system-ui, sans-serif;
|
||||
--font-display: var(--font-abril), Georgia, "Times New Roman", serif;
|
||||
|
||||
--color-border: hsl(var(--border));
|
||||
--color-input: hsl(var(--input));
|
||||
--color-ring: hsl(var(--ring));
|
||||
@@ -28,61 +31,97 @@
|
||||
}
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 45 100% 97%;
|
||||
--foreground: 24 33% 14%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 24 33% 14%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 24 33% 14%;
|
||||
--primary: 34 92% 52%;
|
||||
--primary-foreground: 45 100% 97%;
|
||||
--secondary: 36 45% 92%;
|
||||
--secondary-foreground: 24 33% 14%;
|
||||
--muted: 36 33% 94%;
|
||||
--muted-foreground: 24 10% 42%;
|
||||
--accent: 29 60% 90%;
|
||||
--accent-foreground: 24 33% 14%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--border: 31 28% 84%;
|
||||
--input: 31 28% 84%;
|
||||
--ring: 34 92% 52%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
:root {
|
||||
--background: 48 100% 96%;
|
||||
--foreground: 20 6% 16%;
|
||||
--card: 30 100% 97%;
|
||||
--card-foreground: 20 6% 16%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 20 6% 16%;
|
||||
--primary: 0 72% 42%;
|
||||
--primary-foreground: 48 100% 96%;
|
||||
--secondary: 30 47% 90%;
|
||||
--secondary-foreground: 20 6% 16%;
|
||||
--muted: 28 47% 90%;
|
||||
--muted-foreground: 24 6% 47%;
|
||||
--accent: 28 52% 64%;
|
||||
--accent-foreground: 20 6% 16%;
|
||||
--destructive: 0 72% 50%;
|
||||
--destructive-foreground: 48 100% 96%;
|
||||
--border: 28 24% 87%;
|
||||
--input: 28 24% 87%;
|
||||
--ring: 0 72% 42%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 24 33% 10%;
|
||||
--foreground: 45 100% 96%;
|
||||
--card: 24 29% 14%;
|
||||
--card-foreground: 45 100% 96%;
|
||||
--popover: 24 29% 14%;
|
||||
--popover-foreground: 45 100% 96%;
|
||||
--primary: 34 92% 52%;
|
||||
--primary-foreground: 24 33% 10%;
|
||||
--secondary: 24 20% 20%;
|
||||
--secondary-foreground: 45 100% 96%;
|
||||
--muted: 24 18% 18%;
|
||||
--muted-foreground: 28 15% 66%;
|
||||
--accent: 24 20% 20%;
|
||||
--accent-foreground: 45 100% 96%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--border: 24 18% 24%;
|
||||
--input: 24 18% 24%;
|
||||
--ring: 34 92% 52%;
|
||||
}
|
||||
.dark {
|
||||
--background: 20 9% 10%;
|
||||
--foreground: 40 33% 94%;
|
||||
--card: 24 9% 14%;
|
||||
--card-foreground: 40 33% 94%;
|
||||
--popover: 24 9% 14%;
|
||||
--popover-foreground: 40 33% 94%;
|
||||
--primary: 0 72% 50%;
|
||||
--primary-foreground: 20 9% 10%;
|
||||
--secondary: 24 13% 22%;
|
||||
--secondary-foreground: 40 33% 94%;
|
||||
--muted: 24 6% 25%;
|
||||
--muted-foreground: 24 6% 64%;
|
||||
--accent: 28 82% 32%;
|
||||
--accent-foreground: 40 33% 94%;
|
||||
--destructive: 0 84% 60%;
|
||||
--destructive-foreground: 20 9% 10%;
|
||||
--border: 24 6% 33%;
|
||||
--input: 24 6% 33%;
|
||||
--ring: 0 72% 50%;
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
border-color: hsl(var(--border));
|
||||
}
|
||||
body {
|
||||
background-color: hsl(var(--background));
|
||||
color: hsl(var(--foreground));
|
||||
background-image:
|
||||
radial-gradient(circle at top, rgba(255, 188, 66, 0.10), transparent 34%),
|
||||
radial-gradient(circle at bottom right, rgba(122, 69, 22, 0.08), transparent 28%);
|
||||
}
|
||||
* {
|
||||
border-color: hsl(var(--border));
|
||||
}
|
||||
body {
|
||||
background-color: hsl(var(--background));
|
||||
color: hsl(var(--foreground));
|
||||
background-image:
|
||||
repeating-linear-gradient(
|
||||
90deg,
|
||||
transparent,
|
||||
transparent 2px,
|
||||
rgba(120, 53, 15, 0.015) 2px,
|
||||
rgba(120, 53, 15, 0.015) 4px
|
||||
),
|
||||
radial-gradient(
|
||||
circle at 15% 50%,
|
||||
rgba(217, 119, 6, 0.06) 0%,
|
||||
transparent 50%
|
||||
),
|
||||
radial-gradient(
|
||||
circle at 85% 80%,
|
||||
rgba(185, 28, 28, 0.04) 0%,
|
||||
transparent 40%
|
||||
);
|
||||
}
|
||||
|
||||
.dark body {
|
||||
background-image:
|
||||
repeating-linear-gradient(
|
||||
90deg,
|
||||
transparent,
|
||||
transparent 2px,
|
||||
rgba(251, 191, 36, 0.012) 2px,
|
||||
rgba(251, 191, 36, 0.012) 4px
|
||||
),
|
||||
radial-gradient(
|
||||
circle at 15% 50%,
|
||||
rgba(217, 119, 6, 0.05) 0%,
|
||||
transparent 50%
|
||||
),
|
||||
radial-gradient(
|
||||
circle at 85% 80%,
|
||||
rgba(185, 28, 28, 0.03) 0%,
|
||||
transparent 40%
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+28
-20
@@ -1,29 +1,37 @@
|
||||
import type { Metadata } from 'next';
|
||||
import { Inter } from 'next/font/google';
|
||||
import './globals.css';
|
||||
import { ProvidersWrapper } from '@/components/providers-wrapper';
|
||||
import { Toaster } from 'sonner';
|
||||
import type { Metadata } from "next";
|
||||
import { Abril_Fatface, Inter } from "next/font/google";
|
||||
import "./globals.css";
|
||||
import { Toaster } from "sonner";
|
||||
import { ProvidersWrapper } from "@/components/providers-wrapper";
|
||||
|
||||
const inter = Inter({ subsets: ['latin'] });
|
||||
const inter = Inter({ subsets: ["latin"], variable: "--font-inter" });
|
||||
const abril = Abril_Fatface({
|
||||
weight: "400",
|
||||
subsets: ["latin"],
|
||||
variable: "--font-abril",
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'CoopCredits - Media Rewards',
|
||||
description: 'Earn $COOP tokens for watching content on Plex',
|
||||
title: "CoopCredits — Watch, Earn, Cluck",
|
||||
description:
|
||||
"Earn $COOP tokens for watching content on Plex. The Hoboken Chicken coop rewards its flock.",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<html lang="en" className="dark">
|
||||
<body className={`${inter.className} dark bg-slate-950 text-foreground`}>
|
||||
<ProvidersWrapper>
|
||||
{children}
|
||||
<Toaster position="top-right" />
|
||||
</ProvidersWrapper>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
return (
|
||||
<html lang="en" className="dark">
|
||||
<body
|
||||
className={`${inter.variable} ${abril.variable} font-sans dark bg-background text-foreground antialiased`}
|
||||
>
|
||||
<ProvidersWrapper>
|
||||
{children}
|
||||
<Toaster position="top-right" />
|
||||
</ProvidersWrapper>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { Egg, Loader2, Tv } from "lucide-react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { authApi } from "@/lib/api";
|
||||
import { useStore } from "@/lib/store";
|
||||
import { useEffect, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Card,
|
||||
@@ -12,8 +12,8 @@ import {
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { Loader2, Tv } from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
import { authApi } from "@/lib/api";
|
||||
import { useStore } from "@/lib/store";
|
||||
|
||||
export default function LoginPage() {
|
||||
const router = useRouter();
|
||||
@@ -46,13 +46,13 @@ export default function LoginPage() {
|
||||
|
||||
if (!isMounted) {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-slate-950 p-4">
|
||||
<div className="min-h-screen flex items-center justify-center bg-background p-4">
|
||||
<Card className="w-full max-w-md">
|
||||
<CardHeader className="text-center">
|
||||
<div className="mx-auto w-16 h-16 bg-primary/10 rounded-full flex items-center justify-center mb-4">
|
||||
<Tv className="w-8 h-8 text-primary" />
|
||||
<Egg className="w-8 h-8 text-primary" />
|
||||
</div>
|
||||
<CardTitle className="text-2xl">CoopCoins</CardTitle>
|
||||
<CardTitle className="text-2xl font-display">The Coop</CardTitle>
|
||||
<CardDescription>Loading...</CardDescription>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
@@ -61,15 +61,17 @@ export default function LoginPage() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-slate-950 p-4">
|
||||
<Card className="w-full max-w-md">
|
||||
<div className="min-h-screen flex items-center justify-center bg-background p-4">
|
||||
<Card className="w-full max-w-md border-2">
|
||||
<CardHeader className="text-center">
|
||||
<div className="mx-auto w-16 h-16 bg-primary/10 rounded-full flex items-center justify-center mb-4">
|
||||
<Tv className="w-8 h-8 text-primary" />
|
||||
<Egg className="w-8 h-8 text-primary" />
|
||||
</div>
|
||||
<CardTitle className="text-2xl">CoopCoins</CardTitle>
|
||||
<CardTitle className="text-2xl font-display">
|
||||
Enter the Coop
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
Earn CoopCoins ($COOP) for watching content on Plex
|
||||
Sign in with Plex to start earning $COOP tokens
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
@@ -91,7 +93,7 @@ export default function LoginPage() {
|
||||
)}
|
||||
</Button>
|
||||
<p className="mt-4 text-center text-sm text-muted-foreground">
|
||||
Sign in with your Plex account to start earning rewards
|
||||
Join the flock and get rewarded for your watch time
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
+248
-168
@@ -1,184 +1,264 @@
|
||||
'use client';
|
||||
"use client";
|
||||
|
||||
import Link from 'next/link';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Tv, Wallet, Zap, Shield, ChevronRight, PlayCircle, Info } from 'lucide-react';
|
||||
import {
|
||||
ChevronRight,
|
||||
Coins,
|
||||
Egg,
|
||||
Film,
|
||||
PlayCircle,
|
||||
Shield,
|
||||
Tv,
|
||||
Wallet,
|
||||
Zap,
|
||||
} from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
export default function LandingPage() {
|
||||
return (
|
||||
<div className="min-h-screen bg-background flex flex-col selection:bg-primary selection:text-primary-foreground">
|
||||
{/* Grid Pattern Overlay */}
|
||||
<div className="fixed inset-0 z-0 opacity-[0.08] pointer-events-none"
|
||||
style={{ backgroundImage: 'radial-gradient(circle at 2px 2px, rgba(217, 119, 6, 0.6) 1px, transparent 0)', backgroundSize: '42px 42px' }} />
|
||||
|
||||
{/* Header */}
|
||||
<header className="sticky top-0 z-50 w-full border-b bg-background/80 backdrop-blur-md">
|
||||
<div className="container mx-auto px-4 h-16 flex items-center justify-between">
|
||||
<div className="flex items-center gap-2 group cursor-pointer">
|
||||
<div className="w-8 h-8 bg-primary rounded-lg flex items-center justify-center transition-transform group-hover:rotate-12">
|
||||
<Tv className="w-5 h-5 text-primary-foreground" />
|
||||
</div>
|
||||
<span className="font-bold text-xl tracking-tight">CoopCoins</span>
|
||||
</div>
|
||||
|
||||
<nav className="hidden md:flex items-center gap-8">
|
||||
<a href="#features" className="text-sm font-medium text-muted-foreground hover:text-foreground transition-colors">Features</a>
|
||||
<a href="#how-it-works" className="text-sm font-medium text-muted-foreground hover:text-foreground transition-colors">How it Works</a>
|
||||
<Link href="/login">
|
||||
<Button variant="outline" size="sm" className="rounded-full px-6">
|
||||
Dashboard
|
||||
</Button>
|
||||
</Link>
|
||||
</nav>
|
||||
return (
|
||||
<div className="min-h-screen bg-background flex flex-col selection:bg-primary selection:text-primary-foreground">
|
||||
{/* Grain Pattern Overlay */}
|
||||
<div
|
||||
className="fixed inset-0 z-0 opacity-[0.06] pointer-events-none"
|
||||
style={{
|
||||
backgroundImage:
|
||||
"radial-gradient(circle at 2px 2px, rgba(185, 28, 28, 0.5) 1px, transparent 0)",
|
||||
backgroundSize: "38px 38px",
|
||||
}}
|
||||
/>
|
||||
|
||||
<Link href="/login" className="md:hidden">
|
||||
<Button size="sm" className="rounded-full">Login</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</header>
|
||||
{/* Header */}
|
||||
<header className="sticky top-0 z-50 w-full border-b bg-background/80 backdrop-blur-md">
|
||||
<div className="container mx-auto px-4 h-16 flex items-center justify-between">
|
||||
<div className="flex items-center gap-2 group cursor-pointer">
|
||||
<div className="w-8 h-8 bg-primary rounded-lg flex items-center justify-center transition-transform group-hover:rotate-12">
|
||||
<Egg className="w-5 h-5 text-primary-foreground" />
|
||||
</div>
|
||||
<span className="font-display text-xl tracking-tight">
|
||||
CoopCredits
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<main className="flex-1 relative z-10">
|
||||
{/* Hero Section */}
|
||||
<section className="py-20 md:py-32 overflow-hidden">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="max-w-4xl mx-auto text-center space-y-8">
|
||||
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-primary/10 text-primary text-xs font-bold uppercase tracking-widest border border-primary/20 animate-in fade-in slide-in-from-bottom-4 duration-500">
|
||||
<Zap className="w-3 h-3" />
|
||||
Powered by Solana!
|
||||
</div>
|
||||
|
||||
<h1 className="text-5xl md:text-7xl font-extrabold tracking-tighter leading-[1.1] animate-in fade-in slide-in-from-bottom-8 duration-700 delay-100">
|
||||
Earn CoopCoins for Your Watch Time!
|
||||
</h1>
|
||||
|
||||
<p className="text-xl text-muted-foreground max-w-2xl mx-auto animate-in fade-in slide-in-from-bottom-12 duration-1000 delay-200">
|
||||
Earn CoopCoins ($COOP) automatically for enjoying shows on Plex. Then, use them to request new favorites on Overseer!
|
||||
</p>
|
||||
<nav className="hidden md:flex items-center gap-8">
|
||||
<a
|
||||
href="#features"
|
||||
className="text-sm font-medium text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
Features
|
||||
</a>
|
||||
<a
|
||||
href="#how-it-works"
|
||||
className="text-sm font-medium text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
How it Works
|
||||
</a>
|
||||
<Link href="/login">
|
||||
<Button variant="outline" size="sm" className="rounded-full px-6">
|
||||
Dashboard
|
||||
</Button>
|
||||
</Link>
|
||||
</nav>
|
||||
|
||||
<div className="flex flex-col sm:flex-row items-center justify-center gap-4 pt-4 animate-in fade-in slide-in-from-bottom-16 duration-1000 delay-300">
|
||||
<Link href="/login">
|
||||
<Button size="lg" className="h-14 px-8 text-lg rounded-full shadow-lg shadow-primary/20 group">
|
||||
Get Started Now
|
||||
<ChevronRight className="ml-2 w-5 h-5 transition-transform group-hover:translate-x-1" />
|
||||
</Button>
|
||||
</Link>
|
||||
<a href="#how-it-works">
|
||||
<Button variant="ghost" size="lg" className="h-14 px-8 text-lg rounded-full">
|
||||
Learn More
|
||||
</Button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Abstract background element */}
|
||||
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[800px] h-[400px] bg-primary/15 blur-[120px] rounded-full -z-10 pointer-events-none" />
|
||||
</section>
|
||||
<Link href="/login" className="md:hidden">
|
||||
<Button size="sm" className="rounded-full">
|
||||
Login
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Features Section */}
|
||||
<section id="features" className="py-24 border-y bg-amber-50/80 dark:bg-stone-950/40">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-12">
|
||||
<div className="space-y-4 p-6 rounded-2xl bg-background border transition-all hover:shadow-xl hover:-translate-y-1">
|
||||
<div className="w-12 h-12 rounded-xl bg-primary/10 flex items-center justify-center mb-6">
|
||||
<PlayCircle className="w-6 h-6 text-primary" />
|
||||
</div>
|
||||
<h3 className="text-xl font-bold">Watch to Earn</h3>
|
||||
<p className="text-muted-foreground leading-relaxed">
|
||||
Every minute you watch on Plex earns you CoopCoins ($COOP)! No extra steps, just enjoy.
|
||||
</p>
|
||||
</div>
|
||||
<main className="flex-1 relative z-10">
|
||||
{/* Hero Section */}
|
||||
<section className="py-20 md:py-32 overflow-hidden">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="max-w-4xl mx-auto text-center space-y-8">
|
||||
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-primary/10 text-primary text-xs font-bold uppercase tracking-widest border border-primary/20 animate-in fade-in slide-in-from-bottom-4 duration-500">
|
||||
<Coins className="w-3 h-3" />
|
||||
Blockchain-Powered Barnyard
|
||||
</div>
|
||||
|
||||
<div className="space-y-4 p-6 rounded-2xl bg-background border transition-all hover:shadow-xl hover:-translate-y-1">
|
||||
<div className="w-12 h-12 rounded-xl bg-primary/10 flex items-center justify-center mb-6">
|
||||
<Shield className="w-6 h-6 text-primary" />
|
||||
</div>
|
||||
<h3 className="text-xl font-bold">Solana Powered</h3>
|
||||
<p className="text-muted-foreground leading-relaxed">
|
||||
Your CoopCoins are securely handled on the Solana blockchain – all transparent and super fast.
|
||||
</p>
|
||||
</div>
|
||||
<h1 className="font-display text-5xl md:text-7xl tracking-tight leading-[1.1] animate-in fade-in slide-in-from-bottom-8 duration-700 delay-100">
|
||||
Welcome to the Coop, Friend
|
||||
</h1>
|
||||
|
||||
<div className="space-y-4 p-6 rounded-2xl bg-background border transition-all hover:shadow-xl hover:-translate-y-1">
|
||||
<div className="w-12 h-12 rounded-xl bg-primary/10 flex items-center justify-center mb-6">
|
||||
<Zap className="w-6 h-6 text-primary" />
|
||||
</div>
|
||||
<h3 className="text-xl font-bold">Spend Credits</h3>
|
||||
<p className="text-muted-foreground leading-relaxed">
|
||||
Got enough CoopCoins ($COOP)? Use them to request new movies or shows on Overseer. You help shape our library!
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<p className="text-xl text-muted-foreground max-w-2xl mx-auto animate-in fade-in slide-in-from-bottom-12 duration-1000 delay-200">
|
||||
Earn $COOP tokens automatically for every minute you watch on
|
||||
Plex. Then spend them like golden eggs to request new movies &
|
||||
shows.
|
||||
</p>
|
||||
|
||||
{/* How it Works Section */}
|
||||
<section id="how-it-works" className="py-24">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="max-w-3xl mx-auto space-y-16">
|
||||
<div className="text-center space-y-4">
|
||||
<h2 className="text-3xl md:text-5xl font-bold tracking-tight">Simple. Transparent. Fun.</h2>
|
||||
<p className="text-muted-foreground text-lg italic">"Our way of saying thanks for being part of the Hoboken Chicken family!"</p>
|
||||
</div>
|
||||
<div className="flex flex-col sm:flex-row items-center justify-center gap-4 pt-4 animate-in fade-in slide-in-from-bottom-16 duration-1000 delay-300">
|
||||
<Link href="/login">
|
||||
<Button
|
||||
size="lg"
|
||||
className="h-14 px-8 text-lg rounded-full shadow-lg shadow-primary/20 group"
|
||||
>
|
||||
Join the Flock
|
||||
<ChevronRight className="ml-2 w-5 h-5 transition-transform group-hover:translate-x-1" />
|
||||
</Button>
|
||||
</Link>
|
||||
<a href="#how-it-works">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="lg"
|
||||
className="h-14 px-8 text-lg rounded-full"
|
||||
>
|
||||
How It Works
|
||||
</Button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-12">
|
||||
<div className="flex gap-6 items-start">
|
||||
<div className="flex-none w-10 h-10 rounded-full bg-primary flex items-center justify-center font-bold text-primary-foreground">1</div>
|
||||
<div className="space-y-2 pt-1">
|
||||
<h4 className="text-xl font-bold">Connect your Plex Account</h4>
|
||||
<p className="text-muted-foreground">Just log in with Plex. We'll set up your Solana wallet automatically in the background.</p>
|
||||
</div>
|
||||
</div>
|
||||
{/* Warm glow background element */}
|
||||
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[800px] h-[400px] bg-primary/10 blur-[120px] rounded-full -z-10 pointer-events-none" />
|
||||
</section>
|
||||
|
||||
<div className="flex gap-6 items-start">
|
||||
<div className="flex-none w-10 h-10 rounded-full bg-primary flex items-center justify-center font-bold text-primary-foreground">2</div>
|
||||
<div className="space-y-2 pt-1">
|
||||
<h4 className="text-xl font-bold">Watch your Favorite Content</h4>
|
||||
<p className="text-muted-foreground">Enjoy movies and shows as usual. Tautulli tracks your fun, and CoopCoins ($COOP) land in your wallet.</p>
|
||||
</div>
|
||||
</div>
|
||||
{/* Features Section */}
|
||||
<section id="features" className="py-24 border-y bg-secondary/40">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="text-center mb-16">
|
||||
<h2 className="font-display text-3xl md:text-5xl tracking-tight">
|
||||
What the Coop Offers
|
||||
</h2>
|
||||
<p className="text-muted-foreground mt-4 max-w-xl mx-auto">
|
||||
A little barnyard magic mixed with blockchain tech.
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-12">
|
||||
<div className="space-y-4 p-6 rounded-2xl bg-background border-2 transition-all hover:shadow-xl hover:-translate-y-1">
|
||||
<div className="w-12 h-12 rounded-xl bg-primary/10 flex items-center justify-center mb-6">
|
||||
<PlayCircle className="w-6 h-6 text-primary" />
|
||||
</div>
|
||||
<h3 className="text-xl font-bold">Watch & Cluck</h3>
|
||||
<p className="text-muted-foreground leading-relaxed">
|
||||
Every minute on Plex earns $COOP. No extra steps — just kick
|
||||
back and enjoy the show.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-6 items-start">
|
||||
<div className="flex-none w-10 h-10 rounded-full bg-primary flex items-center justify-center font-bold text-primary-foreground">3</div>
|
||||
<div className="space-y-2 pt-1">
|
||||
<h4 className="text-xl font-bold">Redeem and Repeat</h4>
|
||||
<p className="text-muted-foreground">Spend your CoopCoins ($COOP) to pick new things for the server via our Overseer integration. Happy requesting!</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-4 p-6 rounded-2xl bg-background border-2 transition-all hover:shadow-xl hover:-translate-y-1">
|
||||
<div className="w-12 h-12 rounded-xl bg-primary/10 flex items-center justify-center mb-6">
|
||||
<Shield className="w-6 h-6 text-primary" />
|
||||
</div>
|
||||
<h3 className="text-xl font-bold">Solana Strong</h3>
|
||||
<p className="text-muted-foreground leading-relaxed">
|
||||
Your $COOP lives on the Solana blockchain — fast, transparent,
|
||||
and secure as a locked coop.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-card border p-8 rounded-3xl flex flex-col md:flex-row items-center gap-8 shadow-2xl shadow-primary/5">
|
||||
<div className="flex-1 space-y-4 text-center md:text-left">
|
||||
<h3 className="text-2xl font-bold">Ready to start earning?</h3>
|
||||
<p className="text-muted-foreground">Come join our family and friends earning rewards for their watch time!</p>
|
||||
</div>
|
||||
<Link href="/login">
|
||||
<Button size="lg" className="rounded-full px-8 h-12">Start Now</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<div className="space-y-4 p-6 rounded-2xl bg-background border-2 transition-all hover:shadow-xl hover:-translate-y-1">
|
||||
<div className="w-12 h-12 rounded-xl bg-primary/10 flex items-center justify-center mb-6">
|
||||
<Zap className="w-6 h-6 text-primary" />
|
||||
</div>
|
||||
<h3 className="text-xl font-bold">Spend Your Eggs</h3>
|
||||
<p className="text-muted-foreground leading-relaxed">
|
||||
Cash in $COOP to request new movies or shows on Overseer. You
|
||||
help build the library.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Footer */}
|
||||
<footer className="border-t py-12 bg-amber-100/60 dark:bg-stone-950/70">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="flex flex-col md:flex-row justify-between items-center gap-8">
|
||||
<div className="flex items-center gap-2 grayscale opacity-50">
|
||||
<Tv className="w-5 h-5" />
|
||||
<span className="font-bold">CoopCoins</span>
|
||||
</div>
|
||||
|
||||
<div className="text-sm text-muted-foreground text-center md:text-right">
|
||||
<p>© {new Date().getFullYear()} Hoboken Chicken. All rights reserved.</p>
|
||||
<p className="mt-1 flex items-center justify-center md:justify-end gap-1">
|
||||
Built with <Zap className="w-3 h-3 text-yellow-500 fill-yellow-500" /> on Solana
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
{/* How it Works Section */}
|
||||
<section id="how-it-works" className="py-24">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="max-w-3xl mx-auto space-y-16">
|
||||
<div className="text-center space-y-4">
|
||||
<h2 className="font-display text-3xl md:text-5xl tracking-tight">
|
||||
Simple as Counting Chickens
|
||||
</h2>
|
||||
<p className="text-muted-foreground text-lg italic">
|
||||
“Our way of saying thanks for being part of the Hoboken
|
||||
Chicken family!”
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-12">
|
||||
<div className="flex gap-6 items-start">
|
||||
<div className="flex-none w-10 h-10 rounded-full bg-primary flex items-center justify-center font-bold text-primary-foreground font-display">
|
||||
1
|
||||
</div>
|
||||
<div className="space-y-2 pt-1">
|
||||
<h4 className="text-xl font-bold">
|
||||
Connect Your Plex Account
|
||||
</h4>
|
||||
<p className="text-muted-foreground">
|
||||
Just log in with Plex. We set up your Solana wallet
|
||||
quietly in the background.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-6 items-start">
|
||||
<div className="flex-none w-10 h-10 rounded-full bg-primary flex items-center justify-center font-bold text-primary-foreground font-display">
|
||||
2
|
||||
</div>
|
||||
<div className="space-y-2 pt-1">
|
||||
<h4 className="text-xl font-bold">Watch Your Favorites</h4>
|
||||
<p className="text-muted-foreground">
|
||||
Enjoy movies and shows as usual. Tautulli tracks your
|
||||
time, and $COOP lands in your wallet.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-6 items-start">
|
||||
<div className="flex-none w-10 h-10 rounded-full bg-primary flex items-center justify-center font-bold text-primary-foreground font-display">
|
||||
3
|
||||
</div>
|
||||
<div className="space-y-2 pt-1">
|
||||
<h4 className="text-xl font-bold">Redeem & Repeat</h4>
|
||||
<p className="text-muted-foreground">
|
||||
Spend $COOP to pick new things for the server via
|
||||
Overseer. Happy requesting!
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-card border-2 p-8 rounded-3xl flex flex-col md:flex-row items-center gap-8 shadow-2xl shadow-primary/5">
|
||||
<div className="flex-1 space-y-4 text-center md:text-left">
|
||||
<h3 className="font-display text-2xl">Ready to roost?</h3>
|
||||
<p className="text-muted-foreground">
|
||||
Come join the flock and earn rewards for your watch time.
|
||||
</p>
|
||||
</div>
|
||||
<Link href="/login">
|
||||
<Button size="lg" className="rounded-full px-8 h-12">
|
||||
Start Now
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
{/* Footer */}
|
||||
<footer className="border-t-2 py-12 bg-secondary/40">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="flex flex-col md:flex-row justify-between items-center gap-8">
|
||||
<div className="flex items-center gap-2 opacity-60">
|
||||
<Egg className="w-5 h-5" />
|
||||
<span className="font-display">CoopCredits</span>
|
||||
</div>
|
||||
|
||||
<div className="text-sm text-muted-foreground text-center md:text-right">
|
||||
<p>
|
||||
© {new Date().getFullYear()} Hoboken Chicken. All rights
|
||||
reserved.
|
||||
</p>
|
||||
<p className="mt-1 flex items-center justify-center md:justify-end gap-1">
|
||||
Built with <Zap className="w-3 h-3 text-primary fill-primary" />{" "}
|
||||
on Solana
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user