From cd0c731f0daa4db8969e2eb2b9561c5f93ce0107 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 23 Mar 2025 18:11:47 -0600 Subject: [PATCH] feat: update LicenseSuccess and ResetLicense pages with improved API endpoints --- .../app/[locale]/license/success/page.tsx | 13 +- .../app/[locale]/license/view/page.tsx | 162 ++++++++++++++++ .../app/[locale]/reset-license/page.tsx | 179 ++++++++++++++---- apps/website/components/pricing.tsx | 2 +- 4 files changed, 318 insertions(+), 38 deletions(-) create mode 100644 apps/website/app/[locale]/license/view/page.tsx diff --git a/apps/website/app/[locale]/license/success/page.tsx b/apps/website/app/[locale]/license/success/page.tsx index 596f73b..45987a5 100644 --- a/apps/website/app/[locale]/license/success/page.tsx +++ b/apps/website/app/[locale]/license/success/page.tsx @@ -41,12 +41,15 @@ export default function LicenseSuccess() { useEffect(() => { setLoading(true); - fetch(`${SERVER_LICENSE_URL}/license/session?sessionId=${sessionId}`, { - method: "GET", - headers: { - "Content-Type": "application/json", + fetch( + `${SERVER_LICENSE_URL}/stripe/get-license-from-session?sessionId=${sessionId}`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + }, }, - }) + ) .then((res) => res.json()) .then((data) => { if (data.error) { diff --git a/apps/website/app/[locale]/license/view/page.tsx b/apps/website/app/[locale]/license/view/page.tsx new file mode 100644 index 0000000..89d5c0b --- /dev/null +++ b/apps/website/app/[locale]/license/view/page.tsx @@ -0,0 +1,162 @@ +import Link from "next/link"; +import { useParams } from "next/navigation"; + +export const SERVER_LICENSE_URL = + process.env.NODE_ENV === "development" + ? "http://localhost:4002/api" + : "https://licenses.dokploy.com"; + +const LicenseCard = ({ license, stripeSuscription }: any) => { + return ( +
+
+ {/* License Information Section */} +
+

+ License Information +

+
+
+

License ID

+

{license.id}

+
+
+

License Key

+

{license.licenseKey}

+
+
+

+ Activation Status +

+

+ {license.activatedAt ? "Activated" : "Not Activated"} +

+
+
+

+ Last Verification +

+

+ {license.lastVerifiedAt || "Not Verified"} +

+
+
+

Created At

+

+ {new Date(license.createdAt).toLocaleDateString()} +

+
+
+

+ Last Updated +

+

+ {new Date(license.updatedAt).toLocaleDateString()} +

+
+
+
+ + {/* Subscription Information Section */} +
+
+

+ Subscription Information +

+ +
+
+
+

+ Subscription ID +

+

{stripeSuscription.id}

+
+
+

Quantity

+

+ {stripeSuscription.quantity} licenses +

+
+
+

+ Billing Type +

+

+ {stripeSuscription.billingType} +

+
+
+

+ Stripe Customer ID +

+

{license.stripeCustomerId}

+
+
+
+
+
+ ); +}; + +export const ViewLicensePage = async ({ + searchParams, +}: { + searchParams: Promise<{ temporalId: string }>; +}) => { + const newParams = await searchParams; + const temporalId = newParams.temporalId; + const licences = await fetch( + `${SERVER_LICENSE_URL}/license/all?temporalId=${temporalId}`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }, + ); + const data = await licences.json(); + + if (!data.success) { + return ( +
+

+ License Details +

+ + {data.error}, Please try again in + + Reset License + + +
+ ); + } + + console.log(data); + + return ( +
+

+ License Details +

+ {data?.licenses?.map((item: any) => ( + + ))} +
+ ); +}; + +export default ViewLicensePage; diff --git a/apps/website/app/[locale]/reset-license/page.tsx b/apps/website/app/[locale]/reset-license/page.tsx index b6be0d5..9bb8e43 100644 --- a/apps/website/app/[locale]/reset-license/page.tsx +++ b/apps/website/app/[locale]/reset-license/page.tsx @@ -4,19 +4,38 @@ import { Container } from "@/components/Container"; import { SERVER_LICENSE_URL } from "@/components/pricing"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; +import { + InputOTP, + InputOTPGroup, + InputOTPSlot, +} from "@/components/ui/input-otp"; +import { ArrowLeft, ExternalLink } from "lucide-react"; +import { useRouter } from "next/navigation"; import { useState } from "react"; import { toast } from "sonner"; + +interface License { + email: string; + serverIp?: string[]; + licenseKey: string; + productName: string; + createdAt: string; + lastVerifiedAt: string; + billingType: string; + activatedAt: string; + type: string; +} + export default function ResetLicensePage() { + const router = useRouter(); const [email, setEmail] = useState(""); - const [showOtp, setShowOtp] = useState(false); + const [showRender, setShowRender] = useState<"otp" | "email">("email"); const [isLoading, setIsLoading] = useState(false); + const [otp, setOtp] = useState(""); - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - setIsLoading(true); - + const sendEmail = async (email: string) => { try { - const result = await fetch(`${SERVER_LICENSE_URL}/license/verification`, { + const result = await fetch(`${SERVER_LICENSE_URL}/license/send-otp`, { method: "POST", headers: { "Content-Type": "application/json", @@ -25,7 +44,6 @@ export default function ResetLicensePage() { }); const data = await result.json(); - console.log(data); if (data.error) { toast.error( @@ -38,7 +56,52 @@ export default function ResetLicensePage() { toast.success( "We've sent you a code to verify your email. Please check your email for the code.", ); - setShowOtp(true); + setShowRender("otp"); + } + } catch (error) { + toast.error("Something went wrong. Please try again later.", { + duration: 15000, + description: error instanceof Error ? error.message : "Unknown error", + }); + } finally { + setIsLoading(false); + } + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setIsLoading(true); + + await sendEmail(email); + }; + + const handleVerifyOtp = async (e: React.FormEvent) => { + e.preventDefault(); + setIsLoading(true); + + try { + if (otp.length !== 6) { + toast.error("Please enter a valid 6-digit code."); + return; + } + + const result = await fetch(`${SERVER_LICENSE_URL}/license/verify-otp`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ email, otpCode: otp }), + }); + + const data = await result.json(); + if (data.error) { + toast.error("Error verifying code. Please try again.", { + description: data.error, + }); + } else { + const temporalId = data.temporalId; + console.log(temporalId); + router.push(`/license/view?temporalId=${temporalId}`); } } catch (error) { toast.error("Something went wrong. Please try again later.", { @@ -57,33 +120,85 @@ export default function ResetLicensePage() { Reset Your License

- Enter your email address and we'll send you instructions to reset your - license. + {showRender === "otp" + ? "Enter the verification code sent to your email." + : "Enter your email address and we'll send you instructions to reset your license."}

-
-
- setEmail(e.target.value)} - required - className="w-full" - disabled={isLoading} - /> -
- -
+
+ setEmail(e.target.value)} + required + className="w-full" + disabled={isLoading} + /> +
+ + + ) : ( +
+
+ + + + + + + + + + + +
+ + +
+ )} ); diff --git a/apps/website/components/pricing.tsx b/apps/website/components/pricing.tsx index 9cadbc4..2760462 100644 --- a/apps/website/components/pricing.tsx +++ b/apps/website/components/pricing.tsx @@ -147,7 +147,7 @@ export function Pricing() { // Create checkout session const response = await fetch( - `${SERVER_LICENSE_URL!}/create-checkout-session`, + `${SERVER_LICENSE_URL!}/stripe/create-checkout-session`, { method: "POST", headers: {