feat: add canvas-confetti for celebratory animations and implement LicenseSuccess page

This commit is contained in:
Mauricio Siu
2025-03-20 01:24:54 -06:00
parent 3d622c7a53
commit 21dfc49831
5 changed files with 137 additions and 2 deletions

View File

@@ -0,0 +1,111 @@
"use client";
import { Container } from "@/components/Container";
import { Button } from "@/components/ui/button";
import confetti from "canvas-confetti";
import { CheckCircle2, Copy, Terminal } from "lucide-react";
import Link from "next/link";
import { useEffect, useState } from "react";
export default function LicenseSuccess() {
const [copied, setCopied] = useState(false);
// Generate a realistic-looking API key
const apiKey = `dk_live_${Array.from(
crypto.getRandomValues(new Uint8Array(24)),
)
.map((b) => b.toString(16).padStart(2, "0"))
.join("")}`;
useEffect(() => {
// Launch confetti when the page loads
confetti({
particleCount: 150,
spread: 100,
origin: { y: 0.6 },
});
}, []);
const copyToClipboard = () => {
navigator.clipboard.writeText(apiKey);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
return (
<div className="relative min-h-screen bg-gradient-to-b from-black via-zinc-900 to-black">
<div className="absolute inset-0 bg-[url('/grid.svg')] bg-center [mask-image:linear-gradient(180deg,white,rgba(255,255,255,0))]" />
<Container className="relative pt-24 pb-28">
<div className="mx-auto max-w-3xl text-center">
<div className="flex justify-center mb-8">
<div className="rounded-full bg-green-500/10 p-4">
<CheckCircle2 className="h-16 w-16 text-green-500" />
</div>
</div>
<h1 className="text-5xl font-bold tracking-tight text-white sm:text-6xl mb-6">
Thank you for your purchase!
</h1>
<p className="text-xl leading-8 text-zinc-400 mb-12">
Your Dokploy license has been successfully activated. Here's your
API key to get started.
</p>
<div className="bg-black/50 backdrop-blur-sm border border-zinc-800 rounded-xl p-8 mb-12">
<div className="flex flex-col items-center space-y-6">
<Terminal className="h-10 w-10 text-zinc-500" />
<div className="space-y-4 w-full">
<div className="flex items-center justify-between space-x-4 bg-black/50 rounded-lg p-4 border border-zinc-800">
<code className="text-green-500 text-lg font-mono">
{apiKey}
</code>
<Button
variant="outline"
size="icon"
onClick={copyToClipboard}
className="transition-all duration-200 hover:bg-green-500/10 hover:text-green-500"
>
{copied ? (
<CheckCircle2 className="h-5 w-5" />
) : (
<Copy className="h-5 w-5" />
)}
</Button>
</div>
<div className="text-left space-y-3">
<p className="text-zinc-400 text-sm">
To start using your license, add this API key to your
configuration file:
</p>
<pre className="bg-black/50 rounded-lg p-4 overflow-x-auto border border-zinc-800">
<code className="text-sm font-mono text-zinc-300">
{`# .env
DOKPLOY_LICENSE_KEY=${apiKey}`}
</code>
</pre>
</div>
</div>
</div>
</div>
<div className="flex flex-col sm:flex-row justify-center gap-4">
<Link href="https://docs.dokploy.com/docs/core/installation">
<Button
variant="outline"
className="w-full sm:w-auto hover:bg-zinc-800"
>
View Documentation
</Button>
</Link>
<Link href="https://discord.gg/dokploy">
<Button className="w-full sm:w-auto bg-gradient-to-r from-indigo-500 to-purple-500 hover:from-indigo-600 hover:to-purple-600">
Join our Discord
</Button>
</Link>
</div>
</div>
</Container>
</div>
);
}

View File

@@ -167,8 +167,6 @@ export function Pricing() {
const { sessionId } = await response.json();
console.log(sessionId);
// Redirect to Stripe checkout
const { error } = await stripe.redirectToCheckout({
sessionId,

View File

@@ -30,6 +30,7 @@
"@types/turndown": "^5.0.5",
"autoprefixer": "^10.4.12",
"axios": "^1.8.1",
"canvas-confetti": "^1.9.3",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.0",
"framer-motion": "^11.3.19",
@@ -62,6 +63,7 @@
"@babel/parser": "^7.26.9",
"@babel/plugin-syntax-typescript": "^7.25.9",
"@biomejs/biome": "1.7.0",
"@types/canvas-confetti": "^1.9.0",
"@types/react": "18.3.5",
"@types/react-dom": "18.3.0",
"prettier-plugin-tailwindcss": "^0.5.14"

View File

@@ -0,0 +1,8 @@
<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
<path d="M 40 0 L 0 0 0 40" fill="none" stroke="rgba(255,255,255,0.1)" stroke-width="0.5"/>
</pattern>
</defs>
<rect width="100%" height="100%" fill="url(#grid)" />
</svg>

After

Width:  |  Height:  |  Size: 340 B

16
pnpm-lock.yaml generated
View File

@@ -145,6 +145,9 @@ importers:
axios:
specifier: ^1.8.1
version: 1.8.1
canvas-confetti:
specifier: ^1.9.3
version: 1.9.3
class-variance-authority:
specifier: ^0.7.0
version: 0.7.0
@@ -236,6 +239,9 @@ importers:
'@biomejs/biome':
specifier: 1.7.0
version: 1.7.0
'@types/canvas-confetti':
specifier: ^1.9.0
version: 1.9.0
'@types/react':
specifier: 18.3.5
version: 18.3.5
@@ -1693,6 +1699,9 @@ packages:
'@types/acorn@4.0.6':
resolution: {integrity: sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==}
'@types/canvas-confetti@1.9.0':
resolution: {integrity: sha512-aBGj/dULrimR1XDZLtG9JwxX1b4HPRF6CX9Yfwh3NvstZEm1ZL7RBnel4keCPSqs1ANRu1u2Aoz9R+VmtjYuTg==}
'@types/conventional-commits-parser@5.0.0':
resolution: {integrity: sha512-loB369iXNmAZglwWATL+WRe+CRMmmBPtpolYzIebFaX4YA3x+BEfLqhUAV9WanycKI3TG1IMr5bMJDajDKLlUQ==}
@@ -1900,6 +1909,9 @@ packages:
caniuse-lite@1.0.30001679:
resolution: {integrity: sha512-j2YqID/YwpLnKzCmBOS4tlZdWprXm3ZmQLBH9ZBXFOhoxLA46fwyBvx6toCBWBmnuwUY/qB3kEU6gFx8qgCroA==}
canvas-confetti@1.9.3:
resolution: {integrity: sha512-rFfTURMvmVEX1gyXFgn5QMn81bYk70qa0HLzcIOSVEyl57n6o9ItHeBtUSWdvKAPY0xlvBHno4/v3QPrT83q9g==}
ccount@2.0.1:
resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
@@ -5440,6 +5452,8 @@ snapshots:
dependencies:
'@types/estree': 1.0.5
'@types/canvas-confetti@1.9.0': {}
'@types/conventional-commits-parser@5.0.0':
dependencies:
'@types/node': 20.17.16
@@ -5641,6 +5655,8 @@ snapshots:
caniuse-lite@1.0.30001679: {}
canvas-confetti@1.9.3: {}
ccount@2.0.1: {}
chalk@2.4.2: