feat: ko-fi integration for credit purchases
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
"use client";
|
||||
|
||||
import { Coffee, ExternalLink, X } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
|
||||
interface BuyCreditsModalProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
}
|
||||
|
||||
const PRESETS = [
|
||||
{ dollars: 5, credits: 500, label: "Small Feed", emoji: "🌾" },
|
||||
{ dollars: 10, credits: 1000, label: "Big Feed", emoji: "🌽" },
|
||||
{ dollars: 20, credits: 2000, label: "Feast", emoji: "🍗" },
|
||||
];
|
||||
|
||||
export function BuyCreditsModal({ open, onOpenChange }: BuyCreditsModalProps) {
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="rounded-[2rem] border-4 border-foreground bg-card p-0 shadow-[12px_12px_0px_0px_rgba(0,0,0,0.1)] max-w-md">
|
||||
<DialogHeader className="bg-primary/10 border-b-4 border-foreground p-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<DialogTitle className="font-display text-3xl uppercase italic text-primary">
|
||||
Stock the Coop
|
||||
</DialogTitle>
|
||||
<button
|
||||
onClick={() => onOpenChange(false)}
|
||||
className="rounded-full p-2 hover:bg-background transition-colors"
|
||||
>
|
||||
<X className="h-6 w-6" />
|
||||
</button>
|
||||
</div>
|
||||
<p className="text-sm font-bold text-muted-foreground mt-2">
|
||||
100 $COOP per dollar. Use same email as Plex for auto-delivery.
|
||||
</p>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="p-6 space-y-4">
|
||||
{PRESETS.map((preset) => (
|
||||
<a
|
||||
key={preset.dollars}
|
||||
href={`https://ko-fi.com/dustinnewkirk`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center justify-between rounded-2xl border-4 border-foreground bg-accent/10 p-4 shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] hover:translate-x-[2px] hover:translate-y-[2px] hover:shadow-none transition-all"
|
||||
onClick={() => onOpenChange(false)}
|
||||
>
|
||||
<div className="flex items-center gap-4">
|
||||
<span className="text-3xl">{preset.emoji}</span>
|
||||
<div>
|
||||
<div className="font-black text-lg">{preset.label}</div>
|
||||
<div className="text-sm font-bold text-muted-foreground">
|
||||
${preset.dollars} → {preset.credits} $COOP
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ExternalLink className="h-5 w-5 text-muted-foreground" />
|
||||
</a>
|
||||
))}
|
||||
|
||||
<div className="rounded-2xl border-2 border-dashed border-foreground/30 bg-background p-4 text-center">
|
||||
<p className="text-xs font-bold text-muted-foreground uppercase tracking-widest">
|
||||
Any amount works. $1 = 100 $COOP.
|
||||
</p>
|
||||
<a
|
||||
href="https://ko-fi.com/dustinnewkirk"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-2 mt-3 rounded-full bg-[#FF5E5B] px-6 py-3 font-black text-white shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] hover:translate-x-[2px] hover:translate-y-[2px] hover:shadow-none transition-all"
|
||||
onClick={() => onOpenChange(false)}
|
||||
>
|
||||
<Coffee className="h-5 w-5" />
|
||||
Support on Ko-fi
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import {
|
||||
Activity,
|
||||
Coffee,
|
||||
Egg,
|
||||
ExternalLink,
|
||||
History,
|
||||
@@ -19,6 +20,7 @@ import { api, authApi, overseerApi, userApi } from "@/lib/api";
|
||||
import { useStore } from "@/lib/store";
|
||||
import { formatNumber } from "@/lib/utils";
|
||||
import { ActivityFeed } from "./components/ActivityFeed";
|
||||
import { BuyCreditsModal } from "./components/BuyCreditsModal";
|
||||
import { Leaderboard } from "./components/Leaderboard";
|
||||
import { RequestHistory } from "./components/RequestHistory";
|
||||
import { SearchRequestModal } from "./components/SearchRequestModal";
|
||||
@@ -38,6 +40,7 @@ export default function DashboardPage() {
|
||||
const [pendingRequests, setPendingRequests] = useState<any[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [isSearchModalOpen, setIsSearchModalOpen] = useState(false);
|
||||
const [isBuyModalOpen, setIsBuyModalOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isAuthenticated) {
|
||||
@@ -122,6 +125,14 @@ export default function DashboardPage() {
|
||||
<Search className="mr-2 h-5 w-5" />
|
||||
PECK FOR FEED
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setIsBuyModalOpen(true)}
|
||||
className="rounded-full border-2 border-[#FF5E5B] bg-background font-bold text-[#FF5E5B] hover:bg-[#FF5E5B]/10"
|
||||
>
|
||||
<Coffee className="mr-2 h-5 w-5" />
|
||||
BUY FEED
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={handleRefresh}
|
||||
@@ -258,6 +269,7 @@ export default function DashboardPage() {
|
||||
onOpenChange={setIsSearchModalOpen}
|
||||
costs={requestCosts}
|
||||
/>
|
||||
<BuyCreditsModal open={isBuyModalOpen} onOpenChange={setIsBuyModalOpen} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user