From 89ee55a822658d5d17e403bf83257b4b446d32b6 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 23 Mar 2025 13:31:05 -0600 Subject: [PATCH] feat: add copy-to-clipboard functionality and loading state to LicenseSuccess page --- .../app/[locale]/license/success/page.tsx | 219 +++++++++++------- apps/website/components/pricing.tsx | 2 +- apps/website/package.json | 1 + pnpm-lock.yaml | 15 ++ 4 files changed, 158 insertions(+), 79 deletions(-) diff --git a/apps/website/app/[locale]/license/success/page.tsx b/apps/website/app/[locale]/license/success/page.tsx index 4a55da9..e8c4451 100644 --- a/apps/website/app/[locale]/license/success/page.tsx +++ b/apps/website/app/[locale]/license/success/page.tsx @@ -1,31 +1,68 @@ "use client"; import { Container } from "@/components/Container"; +import { SERVER_LICENSE_URL } from "@/components/pricing"; import { Button } from "@/components/ui/button"; import confetti from "canvas-confetti"; -import { CheckCircle2, Copy, Mail, Terminal } from "lucide-react"; +import copy from "copy-to-clipboard"; +import { CheckCircle2, Copy, Loader2, Mail, Terminal } from "lucide-react"; import Link from "next/link"; +import { redirect, useSearchParams } from "next/navigation"; import { useEffect, useState } from "react"; +interface LicenseSessionResponse { + type: "basic" | "professional" | "business"; + billingType: "monthly" | "yearly"; + key: string; +} + export default function LicenseSuccess() { + const [error, setError] = useState(null); 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("")}`; + const [loading, setLoading] = useState(true); + + const query = useSearchParams(); + + const sessionId = query.get("session_id"); + + if (!sessionId) { + redirect("/"); + } + + const [data, setData] = useState(null); + + useEffect(() => { + setLoading(true); + console.log(`${SERVER_LICENSE_URL}/license/session?sessionId=${sessionId}`); + fetch(`${SERVER_LICENSE_URL}/license/session?sessionId=${sessionId}`, { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }) + .then((res) => res.json()) + .then((data) => setData(data)) + .catch((err) => { + console.error(err); + setError(err.message); + }) + .finally(() => { + setLoading(false); + }); + }, [sessionId]); useEffect(() => { // Launch confetti when the page loads - confetti({ - particleCount: 150, - spread: 100, - origin: { y: 0.6 }, - }); - }, []); + if (data) { + confetti({ + particleCount: 150, + spread: 100, + origin: { y: 0.6 }, + }); + } + }, [data]); const copyToClipboard = () => { - navigator.clipboard.writeText(apiKey); + copy(data?.key ?? ""); setCopied(true); setTimeout(() => setCopied(false), 2000); }; @@ -33,86 +70,112 @@ export default function LicenseSuccess() { return (
- - -
-
-
- + {loading ? ( +
+
+ +
+
+ ) : ( +
+
+
+
+ +
-
-

- Thank you for your purchase! -

+

+ Thank you for your purchase! +

-

- Your Dokploy license has been successfully activated. Here's your - API key to get started. -

- -
- -

- We've also sent your API key to your email for safekeeping +

+ Your Dokploy license has been successfully activated. Here's your + API key to get started.

-
-
-
- -
-
- - {apiKey} - - -
+
+ +

+ We've also sent your API key to your email for safekeeping +

+
-
-

- To start using your license, add this API key to your - configuration file: -

-
-										
-											{`# .env
-DOKPLOY_LICENSE_KEY=${apiKey}`}
+						
+
+ +
+
+

+ Steps to enable paid features +

+
    +
  • + 1. Web Server +
  • +
  • + 2. Enable Paid Features +
  • +
  • + + 3. Copy the Key Below and Paste it in the license key + field and click on validate + +
  • +
+
+
+ + {data?.key} -
+ +
-
-
- +
+ + + - - - - +
- + )}
); } diff --git a/apps/website/components/pricing.tsx b/apps/website/components/pricing.tsx index eac3a7e..9cadbc4 100644 --- a/apps/website/components/pricing.tsx +++ b/apps/website/components/pricing.tsx @@ -50,7 +50,7 @@ function SwirlyDoodle(props: React.ComponentPropsWithoutRef<"svg">) { ); } -const SERVER_LICENSE_URL = +export const SERVER_LICENSE_URL = process.env.NODE_ENV === "development" ? "http://localhost:4002/api" : "https://licenses.dokploy.com"; diff --git a/apps/website/package.json b/apps/website/package.json index c49f285..c4628d9 100644 --- a/apps/website/package.json +++ b/apps/website/package.json @@ -12,6 +12,7 @@ }, "browserslist": "defaults, not ie <= 11", "dependencies": { + "copy-to-clipboard": "3.3.3", "@headlessui/react": "^2.2.0", "@headlessui/tailwindcss": "^0.2.0", "@prettier/plugin-xml": "^3.4.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dff7347..7740196 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -154,6 +154,9 @@ importers: clsx: specifier: ^2.1.0 version: 2.1.1 + copy-to-clipboard: + specifier: 3.3.3 + version: 3.3.3 framer-motion: specifier: ^11.3.19 version: 11.3.19(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -2035,6 +2038,9 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + copy-to-clipboard@3.3.3: + resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==} + cosmiconfig-typescript-loader@5.0.0: resolution: {integrity: sha512-+8cK7jRAReYkMwMiG+bxhcNKiHJDM6bR9FD/nGBXOWdMLuYawjF5cGrtLilJ+LGd3ZjCXnJjR5DkfWPoIVlqJA==} engines: {node: '>=v16'} @@ -3643,6 +3649,9 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + toggle-selection@1.0.6: + resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==} + trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} @@ -5780,6 +5789,10 @@ snapshots: convert-source-map@2.0.0: {} + copy-to-clipboard@3.3.3: + dependencies: + toggle-selection: 1.0.6 + cosmiconfig-typescript-loader@5.0.0(@types/node@20.17.16)(cosmiconfig@9.0.0(typescript@5.6.3))(typescript@5.6.3): dependencies: '@types/node': 20.17.16 @@ -7868,6 +7881,8 @@ snapshots: dependencies: is-number: 7.0.0 + toggle-selection@1.0.6: {} + trim-lines@3.0.1: {} trough@2.2.0: {}