Step1ProductSelection.tsx (3868B)
1 import { FormattedMessage, useIntl } from "react-intl"; 2 import { motion } from "framer-motion"; 3 import { useEffect, useRef, useState } from "react"; 4 import { LNCheckout, ProductTemplates } from "./Types"; 5 import { StepHeader } from "./StepHeader"; 6 import { Loader2 } from "lucide-react"; 7 8 export interface Step1ProductSelectionProps { 9 lnCheckout: LNCheckout | null 10 setLNCheckout: (lnCheckout: LNCheckout) => void 11 setError: (error: string | null) => void 12 } 13 14 export function Step1ProductSelection(props: Step1ProductSelectionProps) { 15 const intl = useIntl() 16 const [productTemplates, setProductTemplates] = useState<ProductTemplates | null>(null) // The different product options 17 const isStepDone = props.lnCheckout?.product_template_name != null 18 19 // MARK: - Functions 20 21 const fetchProductTemplates = async () => { 22 try { 23 const response = await fetch(process.env.NEXT_PUBLIC_PURPLE_API_BASE_URL + "/products", { 24 method: 'GET', 25 headers: { 26 'Content-Type': 'application/json' 27 }, 28 }) 29 const data = await response.json() 30 setProductTemplates(data) 31 } 32 catch (e) { 33 console.error(e) 34 props.setError("Failed to get product list from our servers, please try again later in a few minutes. If the problem persists, please contact support.") 35 } 36 } 37 38 const selectProduct = async (productTemplateName: string) => { 39 try { 40 const response = await fetch(process.env.NEXT_PUBLIC_PURPLE_API_BASE_URL + "/ln-checkout", { 41 method: 'POST', 42 headers: { 43 'Content-Type': 'application/json' 44 }, 45 body: JSON.stringify({ product_template_name: productTemplateName }) 46 }) 47 const data: LNCheckout = await response.json() 48 props.setLNCheckout(data) 49 } 50 catch (e) { 51 console.error(e) 52 props.setError("Failed to begin the checkout process. Please wait a few minutes, refresh this page, and try again. If the problem persists, please contact support.") 53 } 54 } 55 56 // MARK: - Effects and hooks 57 58 // Load the products and the LN checkout (if there is one) on page load 59 useEffect(() => { 60 fetchProductTemplates() 61 }, []) 62 63 // MARK: - Render 64 65 return (<> 66 <StepHeader 67 stepNumber={1} 68 title={intl.formatMessage({ id: "purple.checkout.step-1", defaultMessage: "Choose your plan" })} 69 done={isStepDone} 70 active={true} 71 /> 72 <div className="mt-3 mb-4 flex gap-2 items-center"> 73 {productTemplates ? Object.entries(productTemplates).map(([name, productTemplate]) => ( 74 <button 75 key={name} 76 className={`relative flex flex-col items-center justify-center p-3 pt-4 border rounded-lg ${name == props.lnCheckout?.product_template_name ? "border-green-500" : "border-purple-200/50"} disabled:opacity-50 disabled:cursor-not-allowed`} 77 onClick={() => selectProduct(name)} 78 disabled={isStepDone} 79 > 80 {productTemplate.special_label && ( 81 <div className="absolute top-0 right-0 -mt-4 -mr-2 bg-gradient-to-r from-damuspink-500 to-damuspink-600 rounded-full p-1 px-3"> 82 <div className="text-white text-xs font-semibold"> 83 {productTemplate.special_label} 84 </div> 85 </div> 86 )} 87 88 <div className="text-purple-200/50 font-normal text-sm"> 89 {productTemplate.description} 90 </div> 91 <div className="mt-1 text-purple-100/90 font-semibold text-lg"> 92 {productTemplate.amount_msat / 1000} sats 93 </div> 94 </button> 95 )) : ( 96 <div className="flex flex-col items-center justify-center"> 97 <div className="text-purple-200/50 font-normal text-sm flex items-center"> 98 <Loader2 className="w-4 h-4 mr-2 animate-spin" /> 99 Loading... 100 </div> 101 </div> 102 )} 103 </div> 104 </>) 105 }