Add confetti on valid key and make thank you less intrusive

This commit is contained in:
grokdesigns 2025-04-07 21:15:24 -07:00
parent b2faeb3c17
commit 23a68fbc10
No known key found for this signature in database
GPG key ID: 1084CD111FEE75DD
5 changed files with 162 additions and 104 deletions

View file

@ -5,29 +5,41 @@ import confetti from "canvas-confetti";
export default function SupporterMessage({ tier }: { tier: string }) {
return (
<div className="mt-4 text-center">
<div
className="relative inline-block px-2 py-1 bg-white/10 backdrop-blur-md border border-white/20 rounded-md shadow-sm max-w-screen-sm mx-auto cursor-pointer"
<div className="relative flex items-center space-x-2 whitespace-nowrap group">
<span
className="cursor-pointer"
onClick={(e) => {
// Get the bounding box of the element
const rect = (
e.target as HTMLElement
).getBoundingClientRect();
// Calculate the origin based on the top center of the box
// Trigger confetti centered on the word "Pangolin"
confetti({
particleCount: 100,
spread: 70,
origin: {
x: (rect.left + rect.width / 2) / window.innerWidth, // Horizontal center of the box
y: rect.top / window.innerHeight // Top of the box
}
x: (rect.left + rect.width / 2) / window.innerWidth,
y: rect.top / window.innerHeight
},
colors: ["#FFA500", "#FF4500", "#FFD700"]
});
}}
>
<h2 className="text-sm font-semibold text-transparent bg-clip-text bg-gradient-to-r from-orange-400 via-orange-500 to-orange-600 hover:from-orange-600 hover:via-orange-500 hover:to-orange-400">
Pangolin
</span>
{/* SVG Star */}
<svg
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
viewBox="0 0 24 24"
className="w-4 h-4 text-primary"
>
<path d="M12 .587l3.668 7.431 8.2 1.192-5.934 5.782 1.4 8.168L12 18.896l-7.334 3.864 1.4-8.168L.132 9.21l8.2-1.192z" />
</svg>
{/* Popover */}
<div className="absolute left-1/2 transform -translate-x-1/2 -top-10 hidden group-hover:block bg-white/10 backdrop-blur-md text-primary text-sm rounded-md shadow-lg px-4 py-2">
Thank you for supporting Pangolin as a {tier}!
</h2>
</div>
</div>
);

View file

@ -2,20 +2,19 @@
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 20 0.0% 10.0%;
--foreground: 20 0% 10%;
--card: 0 0% 100%;
--card-foreground: 20 0.0% 10.0%;
--card-foreground: 20 0% 10%;
--popover: 0 0% 100%;
--popover-foreground: 20 0.0% 10.0%;
--popover-foreground: 20 0% 10%;
--primary: 24.6 95% 53.1%;
--primary-foreground: 60 9.1% 97.8%;
--secondary: 60 4.8% 95.9%;
--secondary-foreground: 24 9.8% 10%;
--muted: 60 4.8% 85.0%;
--muted: 60 4.8% 85%;
--muted-foreground: 25 5.3% 44.7%;
--accent: 60 4.8% 90%;
--accent-foreground: 24 9.8% 10%;
@ -33,24 +32,24 @@
}
.dark {
--background: 20 0.0% 10.0%;
--background: 20 0% 10%;
--foreground: 60 9.1% 97.8%;
--card: 20 0.0% 10.0%;
--card: 20 0% 10%;
--card-foreground: 60 9.1% 97.8%;
--popover: 20 0.0% 10.0%;
--popover: 20 0% 10%;
--popover-foreground: 60 9.1% 97.8%;
--primary: 20.5 90.2% 48.2%;
--primary-foreground: 60 9.1% 97.8%;
--secondary: 12 6.5% 15.0%;
--secondary: 12 6.5% 15%;
--secondary-foreground: 60 9.1% 97.8%;
--muted: 12 6.5% 25.0%;
--muted: 12 6.5% 25%;
--muted-foreground: 24 5.4% 63.9%;
--accent: 12 2.5% 15.0%;
--accent: 12 2.5% 15%;
--accent-foreground: 60 9.1% 97.8%;
--destructive: 0 72.2% 50.6%;
--destructive-foreground: 60 9.1% 97.8%;
--border: 12 6.5% 30.0%;
--input: 12 6.5% 35.0%;
--border: 12 6.5% 30%;
--input: 12 6.5% 35%;
--ring: 20.5 90.2% 48.2%;
--chart-1: 220 70% 50%;
--chart-2: 160 60% 45%;
@ -60,7 +59,6 @@
}
}
@layer base {
* {
@apply border-border;
@ -70,4 +68,3 @@
@apply bg-background text-foreground;
}
}

View file

@ -62,9 +62,15 @@ export default async function RootLayout({
{/* Footer */}
<footer className="hidden md:block w-full mt-12 py-3 mb-6 px-4">
<div className="container mx-auto flex flex-wrap justify-center items-center h-3 space-x-4 text-sm text-neutral-400 dark:text-neutral-600">
{supporterData?.tier ? (
<SupporterMessage
tier={supporterData.tier}
/>
) : (
<div className="flex items-center space-x-2 whitespace-nowrap">
<span>Pangolin</span>
</div>
)}
<Separator orientation="vertical" />
<a
href="https://fossorial.io/"
@ -114,11 +120,6 @@ export default async function RootLayout({
</>
)}
</div>
{supporterData?.tier && (
<SupporterMessage
tier={supporterData.tier}
/>
)}
</footer>
</SupportStatusProvider>
</EnvProvider>

View file

@ -47,6 +47,7 @@ import {
CardTitle
} from "./ui/card";
import { Check, ExternalLink } from "lucide-react";
import confetti from "canvas-confetti";
const formSchema = z.object({
githubUsername: z
@ -100,6 +101,7 @@ export default function SupporterStatus() {
return;
}
// Trigger the toast
toast({
variant: "default",
title: "Valid Key",
@ -107,6 +109,50 @@ export default function SupporterStatus() {
"Your supporter key has been validated. Thank you for your support!"
});
// Fireworks-style confetti
const duration = 5 * 1000; // 5 seconds
const animationEnd = Date.now() + duration;
const defaults = {
startVelocity: 30,
spread: 360,
ticks: 60,
zIndex: 0,
colors: ["#FFA500", "#FF4500", "#FFD700"] // Orange hues
};
function randomInRange(min: number, max: number) {
return Math.random() * (max - min) + min;
}
const interval = setInterval(() => {
const timeLeft = animationEnd - Date.now();
if (timeLeft <= 0) {
clearInterval(interval);
return;
}
const particleCount = 50 * (timeLeft / duration);
// Launch confetti from two random horizontal positions
confetti({
...defaults,
particleCount,
origin: {
x: randomInRange(0.1, 0.3),
y: Math.random() - 0.2
}
});
confetti({
...defaults,
particleCount,
origin: {
x: randomInRange(0.7, 0.9),
y: Math.random() - 0.2
}
});
}, 250);
setPurchaseOptionsOpen(false);
setKeyOpen(false);
@ -177,7 +223,9 @@ export default function SupporterStatus() {
</p>
<div className="py-6">
<p className="mb-3 text-center">Please select the option that best suits you.</p>
<p className="mb-3 text-center">
Please select the option that best suits you.
</p>
<div className="grid md:grid-cols-2 grid-cols-1 gap-8">
<Card>
<CardHeader>

View file

@ -5,59 +5,59 @@ const config: Config = {
content: [
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
"./src/app/**/*.{js,ts,jsx,tsx,mdx}"
],
theme: {
extend: {
colors: {
background: 'hsl(var(--background))',
foreground: 'hsl(var(--foreground))',
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
card: {
DEFAULT: 'hsl(var(--card))',
foreground: 'hsl(var(--card-foreground))'
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))"
},
popover: {
DEFAULT: 'hsl(var(--popover))',
foreground: 'hsl(var(--popover-foreground))'
DEFAULT: "hsl(var(--popover))",
foreground: "hsl(var(--popover-foreground))"
},
primary: {
DEFAULT: 'hsl(var(--primary))',
foreground: 'hsl(var(--primary-foreground))'
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))"
},
secondary: {
DEFAULT: 'hsl(var(--secondary))',
foreground: 'hsl(var(--secondary-foreground))'
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))"
},
muted: {
DEFAULT: 'hsl(var(--muted))',
foreground: 'hsl(var(--muted-foreground))'
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))"
},
accent: {
DEFAULT: 'hsl(var(--accent))',
foreground: 'hsl(var(--accent-foreground))'
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))"
},
destructive: {
DEFAULT: 'hsl(var(--destructive))',
foreground: 'hsl(var(--destructive-foreground))'
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))"
},
border: 'hsl(var(--border))',
input: 'hsl(var(--input))',
ring: 'hsl(var(--ring))',
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))'
"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)'
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)"
}
}
},
plugins: [require("tailwindcss-animate")],
plugins: [require("tailwindcss-animate")]
};
export default config;