Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 72fd95c53c | |||
| ed774ff833 | |||
| c856c61579 | |||
| 29aa4645a6 |
@@ -7,49 +7,31 @@ import { ServiceWrapper } from "@/components/ServiceWrapper";
|
|||||||
import Tag from "@/tag/Tag";
|
import Tag from "@/tag/Tag";
|
||||||
|
|
||||||
const halant = Halant({
|
const halant = Halant({
|
||||||
variable: "--font-halant",
|
variable: "--font-halant", subsets: ["latin"],
|
||||||
subsets: ["latin"],
|
|
||||||
weight: ["300", "400", "500", "600", "700"],
|
weight: ["300", "400", "500", "600", "700"],
|
||||||
});
|
});
|
||||||
|
|
||||||
const dmSans = DM_Sans({
|
const dmSans = DM_Sans({
|
||||||
variable: "--font-dm-sans",
|
variable: "--font-dm-sans", subsets: ["latin"],
|
||||||
subsets: ["latin"],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const inter = Inter({
|
const inter = Inter({
|
||||||
variable: "--font-inter",
|
variable: "--font-inter", subsets: ["latin"],
|
||||||
subsets: ["latin"],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "DigitalPro - Premium Digital Products & Templates",
|
title: "DigitalPro - Premium Digital Products & Templates", description: "Premium digital templates and tools designed to accelerate your success. Instant access, secure payments, 24/7 support.", keywords: "digital products, templates, Notion templates, AI tools, productivity, business templates, digital assets", metadataBase: new URL("https://digitalpro.com"),
|
||||||
description: "Premium digital templates and tools designed to accelerate your success. Instant access, secure payments, 24/7 support.",
|
|
||||||
keywords: "digital products, templates, Notion templates, AI tools, productivity, business templates, digital assets",
|
|
||||||
metadataBase: new URL("https://digitalpro.com"),
|
|
||||||
alternates: {
|
alternates: {
|
||||||
canonical: "https://digitalpro.com",
|
canonical: "https://digitalpro.com"},
|
||||||
},
|
|
||||||
openGraph: {
|
openGraph: {
|
||||||
title: "DigitalPro - Premium Digital Products",
|
title: "DigitalPro - Premium Digital Products", description: "Premium digital templates and tools to transform your business", url: "https://digitalpro.com", siteName: "DigitalPro", type: "website", images: [
|
||||||
description: "Premium digital templates and tools to transform your business",
|
|
||||||
url: "https://digitalpro.com",
|
|
||||||
siteName: "DigitalPro",
|
|
||||||
type: "website",
|
|
||||||
images: [
|
|
||||||
{
|
{
|
||||||
url: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AaqWADaE26HPuu9McAi5WY1Ok3/modern-digital-product-showcase-with-abs-1772864653378-8b3bdb2c.png",
|
url: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AaqWADaE26HPuu9McAi5WY1Ok3/modern-digital-product-showcase-with-abs-1772864653378-8b3bdb2c.png", alt: "DigitalPro Premium Templates"},
|
||||||
alt: "DigitalPro Premium Templates",
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
twitter: {
|
twitter: {
|
||||||
card: "summary_large_image",
|
card: "summary_large_image", title: "DigitalPro - Premium Digital Products", description: "Premium templates and tools to accelerate your success", images: [
|
||||||
title: "DigitalPro - Premium Digital Products",
|
"https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AaqWADaE26HPuu9McAi5WY1Ok3/modern-digital-product-showcase-with-abs-1772864653378-8b3bdb2c.png"],
|
||||||
description: "Premium templates and tools to accelerate your success",
|
|
||||||
images: [
|
|
||||||
"https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AaqWADaE26HPuu9McAi5WY1Ok3/modern-digital-product-showcase-with-abs-1772864653378-8b3bdb2c.png",
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
robots: {
|
robots: {
|
||||||
index: true,
|
index: true,
|
||||||
|
|||||||
463
src/app/page.tsx
463
src/app/page.tsx
@@ -21,6 +21,10 @@ import {
|
|||||||
Sparkles,
|
Sparkles,
|
||||||
Star,
|
Star,
|
||||||
Crown,
|
Crown,
|
||||||
|
Lock,
|
||||||
|
CheckCircle,
|
||||||
|
AlertCircle,
|
||||||
|
Eye,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
|
||||||
const navItems = [
|
const navItems = [
|
||||||
@@ -33,8 +37,7 @@ const navItems = [
|
|||||||
|
|
||||||
const footerColumns = [
|
const footerColumns = [
|
||||||
{
|
{
|
||||||
title: "Product",
|
title: "Product", items: [
|
||||||
items: [
|
|
||||||
{ label: "Templates", href: "/products" },
|
{ label: "Templates", href: "/products" },
|
||||||
{ label: "Pricing", href: "/pricing" },
|
{ label: "Pricing", href: "/pricing" },
|
||||||
{ label: "Features", href: "#features" },
|
{ label: "Features", href: "#features" },
|
||||||
@@ -42,8 +45,7 @@ const footerColumns = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Company",
|
title: "Company", items: [
|
||||||
items: [
|
|
||||||
{ label: "About", href: "#" },
|
{ label: "About", href: "#" },
|
||||||
{ label: "Blog", href: "#" },
|
{ label: "Blog", href: "#" },
|
||||||
{ label: "Careers", href: "#" },
|
{ label: "Careers", href: "#" },
|
||||||
@@ -51,8 +53,7 @@ const footerColumns = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Legal",
|
title: "Legal", items: [
|
||||||
items: [
|
|
||||||
{ label: "Privacy", href: "#" },
|
{ label: "Privacy", href: "#" },
|
||||||
{ label: "Terms", href: "#" },
|
{ label: "Terms", href: "#" },
|
||||||
{ label: "Cookies", href: "#" },
|
{ label: "Cookies", href: "#" },
|
||||||
@@ -62,6 +63,223 @@ const footerColumns = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export default function HomePage() {
|
export default function HomePage() {
|
||||||
|
// Payment Verification and Tokenization Handler
|
||||||
|
const handlePaymentVerification = async (paymentData: any) => {
|
||||||
|
try {
|
||||||
|
// Tokenize sensitive payment data - never store raw card numbers
|
||||||
|
const tokenizedData = await tokenizePaymentData(paymentData);
|
||||||
|
|
||||||
|
// Verify tokenized payment
|
||||||
|
const verificationResult = await verifyPayment(tokenizedData);
|
||||||
|
|
||||||
|
if (verificationResult.success) {
|
||||||
|
// Process verified payment
|
||||||
|
await processVerifiedPayment(verificationResult);
|
||||||
|
return { status: "success", message: "Payment processed successfully" };
|
||||||
|
} else {
|
||||||
|
// Handle verification failure
|
||||||
|
return handlePaymentError(verificationResult.error);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Comprehensive error handling
|
||||||
|
return handlePaymentException(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Tokenization: Convert raw payment data to secure tokens
|
||||||
|
// PCI-DSS Compliance: Reduces PCI compliance burden by not handling raw card data
|
||||||
|
const tokenizePaymentData = async (paymentData: any) => {
|
||||||
|
const { cardNumber, cvv, expiryDate, holderName } = paymentData;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Call tokenization service (e.g., Stripe, Square, PayPal API)
|
||||||
|
const response = await fetch("/api/tokenize", {
|
||||||
|
method: "POST", headers: {
|
||||||
|
"Content-Type": "application/json", "Authorization": `Bearer ${process.env.NEXT_PUBLIC_PAYMENT_API_KEY}`,
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
cardNumber: cardNumber.replace(/\s/g, ""), // Remove formatting
|
||||||
|
cvv: cvv, // CVV should be transmitted only once for tokenization
|
||||||
|
expiryDate: expiryDate,
|
||||||
|
holderName: holderName,
|
||||||
|
// Add timestamp to prevent replay attacks
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
// Include nonce for CSRF protection
|
||||||
|
nonce: generateNonce(),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Tokenization failed: ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const tokenizedResult = await response.json();
|
||||||
|
|
||||||
|
// Ensure token is secure and time-limited
|
||||||
|
if (!tokenizedResult.token || !tokenizedResult.expiresAt) {
|
||||||
|
throw new Error("Invalid tokenization response");
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
token: tokenizedResult.token,
|
||||||
|
expiresAt: tokenizedResult.expiresAt,
|
||||||
|
tokenType: "payment_token"};
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Tokenization error: ${error}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Payment Verification: Validate tokenized payment against fraud checks
|
||||||
|
const verifyPayment = async (tokenizedData: any) => {
|
||||||
|
const { token, expiresAt } = tokenizedData;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Check token expiration
|
||||||
|
if (new Date(expiresAt) < new Date()) {
|
||||||
|
throw new Error("Payment token has expired");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call payment verification service
|
||||||
|
const response = await fetch("/api/verify-payment", {
|
||||||
|
method: "POST", headers: {
|
||||||
|
"Content-Type": "application/json", "Authorization": `Bearer ${process.env.NEXT_PUBLIC_PAYMENT_API_KEY}`,
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
token: token,
|
||||||
|
amount: 0, // Amount should be validated separately
|
||||||
|
currency: "USD", // Include verification details for fraud prevention
|
||||||
|
verification: {
|
||||||
|
cvvCheck: true,
|
||||||
|
avsCheck: true,
|
||||||
|
threeDSecure: true,
|
||||||
|
},
|
||||||
|
// Metadata for audit trail
|
||||||
|
metadata: {
|
||||||
|
userId: "user_id_here", orderId: "order_id_here", timestamp: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Payment verification failed: ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const verificationResult = await response.json();
|
||||||
|
|
||||||
|
// Validate verification response structure
|
||||||
|
if (!verificationResult.verified && !verificationResult.error) {
|
||||||
|
throw new Error("Invalid verification response structure");
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: verificationResult.verified,
|
||||||
|
transactionId: verificationResult.transactionId || null,
|
||||||
|
error: verificationResult.error || null,
|
||||||
|
fraudScore: verificationResult.fraudScore || 0,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Verification error: ${error}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Process Verified Payment: Execute transaction with verified token
|
||||||
|
const processVerifiedPayment = async (verificationResult: any) => {
|
||||||
|
const { transactionId, fraudScore } = verificationResult;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Check fraud score threshold (0-100, higher = more suspicious)
|
||||||
|
if (fraudScore > 75) {
|
||||||
|
throw new Error("Payment flagged as high fraud risk");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the verified payment
|
||||||
|
const response = await fetch("/api/process-payment", {
|
||||||
|
method: "POST", headers: {
|
||||||
|
"Content-Type": "application/json", "Authorization": `Bearer ${process.env.NEXT_PUBLIC_PAYMENT_API_KEY}`,
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
transactionId: transactionId,
|
||||||
|
fraudScore: fraudScore,
|
||||||
|
// PCI-DSS: Include settlement strategy
|
||||||
|
settlement: {
|
||||||
|
method: "direct", schedule: "immediate"},
|
||||||
|
// Include receipt and audit information
|
||||||
|
receipt: {
|
||||||
|
sendTo: "customer_email", includeInvoice: true,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Payment processing failed: ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const processResult = await response.json();
|
||||||
|
|
||||||
|
// Log transaction for audit and compliance
|
||||||
|
await logTransactionAudit({
|
||||||
|
transactionId: processResult.transactionId,
|
||||||
|
status: "completed", timestamp: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
|
||||||
|
return processResult;
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Processing error: ${error}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Error Handling: Specific error responses for different scenarios
|
||||||
|
const handlePaymentError = (error: any) => {
|
||||||
|
const errorMap: { [key: string]: string } = {
|
||||||
|
"insufficient_funds": "Your account has insufficient funds. Please check your balance.", "card_declined": "Your card was declined. Please verify your card details.", "expired_card": "Your card has expired. Please use a valid card.", "invalid_cvv": "The CVV provided is invalid. Please check and try again.", "fraud_detected": "This transaction was flagged for fraud prevention. Please contact support.", "duplicate_transaction": "A similar transaction was recently processed. Please try again later.", "network_error": "Network error occurred. Please try again."};
|
||||||
|
|
||||||
|
const errorMessage = errorMap[error] || "Payment verification failed. Please try again.";
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: "error", message: errorMessage,
|
||||||
|
code: error,
|
||||||
|
// Include retry information
|
||||||
|
retry: {
|
||||||
|
allowed: !["card_declined", "expired_card", "insufficient_funds"].includes(error),
|
||||||
|
afterSeconds: 30,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Exception Handling: Catch unexpected errors and provide user-friendly message
|
||||||
|
const handlePaymentException = (error: any) => {
|
||||||
|
console.error("Payment exception:", error);
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: "error", message: "An unexpected error occurred during payment processing. Please try again or contact support.", code: "PAYMENT_EXCEPTION", debug: process.env.NODE_ENV === "development" ? error.message : undefined,
|
||||||
|
retry: {
|
||||||
|
allowed: true,
|
||||||
|
afterSeconds: 60,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Generate nonce for CSRF protection
|
||||||
|
const generateNonce = (): string => {
|
||||||
|
return Math.random().toString(36).substr(2) + Date.now().toString(36);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Audit logging for PCI-DSS compliance
|
||||||
|
const logTransactionAudit = async (auditData: any) => {
|
||||||
|
try {
|
||||||
|
await fetch("/api/audit-log", {
|
||||||
|
method: "POST", headers: {
|
||||||
|
"Content-Type": "application/json"},
|
||||||
|
body: JSON.stringify({
|
||||||
|
...auditData,
|
||||||
|
type: "payment_transaction", ipAddress: "ip_here", // Should be captured server-side
|
||||||
|
userAgent: typeof window !== "undefined" ? navigator.userAgent : ""}),
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Audit logging failed:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeProvider
|
<ThemeProvider
|
||||||
defaultButtonVariant="icon-arrow"
|
defaultButtonVariant="icon-arrow"
|
||||||
@@ -92,19 +310,13 @@ export default function HomePage() {
|
|||||||
slides={[
|
slides={[
|
||||||
{
|
{
|
||||||
imageSrc:
|
imageSrc:
|
||||||
"https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AaqWADaE26HPuu9McAi5WY1Ok3/modern-digital-product-showcase-with-abs-1772864653378-8b3bdb2c.png",
|
"https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AaqWADaE26HPuu9McAi5WY1Ok3/modern-digital-product-showcase-with-abs-1772864653378-8b3bdb2c.png", imageAlt: "Modern digital product showcase"},
|
||||||
imageAlt: "Modern digital product showcase",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
imageSrc:
|
imageSrc:
|
||||||
"https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AaqWADaE26HPuu9McAi5WY1Ok3/digital-marketplace-dashboard-with-glowi-1772864653980-c6766c2a.png",
|
"https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AaqWADaE26HPuu9McAi5WY1Ok3/digital-marketplace-dashboard-with-glowi-1772864653980-c6766c2a.png", imageAlt: "Digital marketplace dashboard"},
|
||||||
imageAlt: "Digital marketplace dashboard",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
imageSrc:
|
imageSrc:
|
||||||
"https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AaqWADaE26HPuu9McAi5WY1Ok3/abstract-digital-transformation-concept--1772864653127-49ec1355.png",
|
"https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AaqWADaE26HPuu9McAi5WY1Ok3/abstract-digital-transformation-concept--1772864653127-49ec1355.png", imageAlt: "Digital transformation concept"},
|
||||||
imageAlt: "Digital transformation concept",
|
|
||||||
},
|
|
||||||
]}
|
]}
|
||||||
autoplayDelay={5000}
|
autoplayDelay={5000}
|
||||||
showDimOverlay={true}
|
showDimOverlay={true}
|
||||||
@@ -120,40 +332,28 @@ export default function HomePage() {
|
|||||||
features={[
|
features={[
|
||||||
{
|
{
|
||||||
icon: Zap,
|
icon: Zap,
|
||||||
title: "Instant Access",
|
title: "Instant Access", description:
|
||||||
description:
|
"Get immediate access to all your purchased digital products and templates"},
|
||||||
"Get immediate access to all your purchased digital products and templates",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
icon: Shield,
|
icon: Shield,
|
||||||
title: "Secure & Safe",
|
title: "Secure & Safe", description:
|
||||||
description:
|
"Your payments and data are protected with industry-leading security standards"},
|
||||||
"Your payments and data are protected with industry-leading security standards",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
icon: HeadphonesIcon,
|
icon: HeadphonesIcon,
|
||||||
title: "24/7 Support",
|
title: "24/7 Support", description:
|
||||||
description:
|
"Our dedicated team is always ready to help with any questions or issues"},
|
||||||
"Our dedicated team is always ready to help with any questions or issues",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
icon: Zap,
|
icon: Zap,
|
||||||
title: "Regular Updates",
|
title: "Regular Updates", description:
|
||||||
description:
|
"Continuous improvements and new templates added to our collection"},
|
||||||
"Continuous improvements and new templates added to our collection",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
icon: TrendingUp,
|
icon: TrendingUp,
|
||||||
title: "Proven Results",
|
title: "Proven Results", description:
|
||||||
description:
|
"Thousands of users have transformed their business with our templates"},
|
||||||
"Thousands of users have transformed their business with our templates",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
icon: Award,
|
icon: Award,
|
||||||
title: "Premium Quality",
|
title: "Premium Quality", description:
|
||||||
description:
|
"All templates are crafted by industry experts with attention to detail"},
|
||||||
"All templates are crafted by industry experts with attention to detail",
|
|
||||||
},
|
|
||||||
]}
|
]}
|
||||||
animationType="slide-up"
|
animationType="slide-up"
|
||||||
textboxLayout="default"
|
textboxLayout="default"
|
||||||
@@ -169,60 +369,25 @@ export default function HomePage() {
|
|||||||
tag="Popular"
|
tag="Popular"
|
||||||
products={[
|
products={[
|
||||||
{
|
{
|
||||||
id: "1",
|
id: "1", brand: "DigitalPro", name: "Project Management Master", price: "$49.99", rating: 5,
|
||||||
brand: "DigitalPro",
|
reviewCount: "2.3k", imageSrc:
|
||||||
name: "Project Management Master",
|
"https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AaqWADaE26HPuu9McAi5WY1Ok3/premium-notion-template-dashboard-for-pr-1772864654183-63b422bf.png?_wi=1", imageAlt: "Project Management Notion Template"},
|
||||||
price: "$49.99",
|
|
||||||
rating: 5,
|
|
||||||
reviewCount: "2.3k",
|
|
||||||
imageSrc:
|
|
||||||
"https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AaqWADaE26HPuu9McAi5WY1Ok3/premium-notion-template-dashboard-for-pr-1772864654183-63b422bf.png?_wi=1",
|
|
||||||
imageAlt: "Project Management Notion Template",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: "2",
|
id: "2", brand: "DigitalPro", name: "AI Content Suite Pro", price: "$79.99", rating: 5,
|
||||||
brand: "DigitalPro",
|
reviewCount: "1.8k", imageSrc:
|
||||||
name: "AI Content Suite Pro",
|
"https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AaqWADaE26HPuu9McAi5WY1Ok3/premium-ai-content-creation-toolkit-inte-1772864653758-1b13a7eb.png?_wi=1", imageAlt: "AI Content Creation Toolkit"},
|
||||||
price: "$79.99",
|
|
||||||
rating: 5,
|
|
||||||
reviewCount: "1.8k",
|
|
||||||
imageSrc:
|
|
||||||
"https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AaqWADaE26HPuu9McAi5WY1Ok3/premium-ai-content-creation-toolkit-inte-1772864653758-1b13a7eb.png?_wi=1",
|
|
||||||
imageAlt: "AI Content Creation Toolkit",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: "3",
|
id: "3", brand: "DigitalPro", name: "Financial Freedom Planner", price: "$39.99", rating: 4,
|
||||||
brand: "DigitalPro",
|
reviewCount: "1.5k", imageSrc:
|
||||||
name: "Financial Freedom Planner",
|
"https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AaqWADaE26HPuu9McAi5WY1Ok3/premium-financial-planning-and-budgeting-1772864653112-bbdb3d99.png?_wi=1", imageAlt: "Financial Planning Template"},
|
||||||
price: "$39.99",
|
|
||||||
rating: 4,
|
|
||||||
reviewCount: "1.5k",
|
|
||||||
imageSrc:
|
|
||||||
"https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AaqWADaE26HPuu9McAi5WY1Ok3/premium-financial-planning-and-budgeting-1772864653112-bbdb3d99.png?_wi=1",
|
|
||||||
imageAlt: "Financial Planning Template",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: "4",
|
id: "4", brand: "DigitalPro", name: "E-Commerce Store Builder", price: "$89.99", rating: 5,
|
||||||
brand: "DigitalPro",
|
reviewCount: "2.1k", imageSrc:
|
||||||
name: "E-Commerce Store Builder",
|
"https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AaqWADaE26HPuu9McAi5WY1Ok3/premium-e-commerce-store-builder-templat-1772864653952-5be26115.png?_wi=1", imageAlt: "E-Commerce Store Template"},
|
||||||
price: "$89.99",
|
|
||||||
rating: 5,
|
|
||||||
reviewCount: "2.1k",
|
|
||||||
imageSrc:
|
|
||||||
"https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AaqWADaE26HPuu9McAi5WY1Ok3/premium-e-commerce-store-builder-templat-1772864653952-5be26115.png?_wi=1",
|
|
||||||
imageAlt: "E-Commerce Store Template",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: "5",
|
id: "5", brand: "DigitalPro", name: "Social Media Manager Hub", price: "$59.99", rating: 4,
|
||||||
brand: "DigitalPro",
|
reviewCount: "1.9k", imageSrc:
|
||||||
name: "Social Media Manager Hub",
|
"https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AaqWADaE26HPuu9McAi5WY1Ok3/premium-social-media-management-suite-wi-1772864656333-10d8a93b.png?_wi=1", imageAlt: "Social Media Management Suite"},
|
||||||
price: "$59.99",
|
|
||||||
rating: 4,
|
|
||||||
reviewCount: "1.9k",
|
|
||||||
imageSrc:
|
|
||||||
"https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AaqWADaE26HPuu9McAi5WY1Ok3/premium-social-media-management-suite-wi-1772864656333-10d8a93b.png?_wi=1",
|
|
||||||
imageAlt: "Social Media Management Suite",
|
|
||||||
},
|
|
||||||
]}
|
]}
|
||||||
gridVariant="uniform-all-items-equal"
|
gridVariant="uniform-all-items-equal"
|
||||||
animationType="slide-up"
|
animationType="slide-up"
|
||||||
@@ -240,48 +405,22 @@ export default function HomePage() {
|
|||||||
tag="Pricing"
|
tag="Pricing"
|
||||||
plans={[
|
plans={[
|
||||||
{
|
{
|
||||||
id: "1",
|
id: "1", badge: "Starter", price: "$0", subtitle: "Perfect for beginners", badgeIcon: Sparkles,
|
||||||
badge: "Starter",
|
|
||||||
price: "$0",
|
|
||||||
subtitle: "Perfect for beginners",
|
|
||||||
badgeIcon: Sparkles,
|
|
||||||
buttons: [{ text: "Get Started", href: "/contact" }],
|
buttons: [{ text: "Get Started", href: "/contact" }],
|
||||||
features: [
|
features: [
|
||||||
"5 Free Templates",
|
"5 Free Templates", "Basic Support", "Community Access", "Monthly Updates"],
|
||||||
"Basic Support",
|
|
||||||
"Community Access",
|
|
||||||
"Monthly Updates",
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "2",
|
id: "2", badge: "Professional", price: "$29.99", subtitle: "Most popular choice", badgeIcon: Star,
|
||||||
badge: "Professional",
|
|
||||||
price: "$29.99",
|
|
||||||
subtitle: "Most popular choice",
|
|
||||||
badgeIcon: Star,
|
|
||||||
buttons: [{ text: "Subscribe Now", href: "/contact" }],
|
buttons: [{ text: "Subscribe Now", href: "/contact" }],
|
||||||
features: [
|
features: [
|
||||||
"Unlimited Templates",
|
"Unlimited Templates", "Priority Support", "Advanced Analytics", "Weekly Updates", "Commercial Rights"],
|
||||||
"Priority Support",
|
|
||||||
"Advanced Analytics",
|
|
||||||
"Weekly Updates",
|
|
||||||
"Commercial Rights",
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "3",
|
id: "3", badge: "Enterprise", price: "$99.99", subtitle: "For large teams", badgeIcon: Crown,
|
||||||
badge: "Enterprise",
|
|
||||||
price: "$99.99",
|
|
||||||
subtitle: "For large teams",
|
|
||||||
badgeIcon: Crown,
|
|
||||||
buttons: [{ text: "Contact Sales", href: "/contact" }],
|
buttons: [{ text: "Contact Sales", href: "/contact" }],
|
||||||
features: [
|
features: [
|
||||||
"Everything in Pro",
|
"Everything in Pro", "Dedicated Account Manager", "Custom Templates", "API Access", "White-label Options"],
|
||||||
"Dedicated Account Manager",
|
|
||||||
"Custom Templates",
|
|
||||||
"API Access",
|
|
||||||
"White-label Options",
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
animationType="slide-up"
|
animationType="slide-up"
|
||||||
@@ -298,45 +437,21 @@ export default function HomePage() {
|
|||||||
tag="Testimonials"
|
tag="Testimonials"
|
||||||
testimonials={[
|
testimonials={[
|
||||||
{
|
{
|
||||||
id: "1",
|
id: "1", name: "Sarah Johnson", role: "Founder", company: "TechStartup Co", rating: 5,
|
||||||
name: "Sarah Johnson",
|
|
||||||
role: "Founder",
|
|
||||||
company: "TechStartup Co",
|
|
||||||
rating: 5,
|
|
||||||
imageSrc:
|
imageSrc:
|
||||||
"https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AaqWADaE26HPuu9McAi5WY1Ok3/professional-headshot-of-a-successful-en-1772864652282-f00d4c62.png",
|
"https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AaqWADaE26HPuu9McAi5WY1Ok3/professional-headshot-of-a-successful-en-1772864652282-f00d4c62.png", imageAlt: "Sarah Johnson"},
|
||||||
imageAlt: "Sarah Johnson",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: "2",
|
id: "2", name: "Michael Chen", role: "Product Manager", company: "InnovateLab", rating: 5,
|
||||||
name: "Michael Chen",
|
|
||||||
role: "Product Manager",
|
|
||||||
company: "InnovateLab",
|
|
||||||
rating: 5,
|
|
||||||
imageSrc:
|
imageSrc:
|
||||||
"https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AaqWADaE26HPuu9McAi5WY1Ok3/professional-headshot-of-a-confident-fem-1772864652004-ed832249.png",
|
"https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AaqWADaE26HPuu9McAi5WY1Ok3/professional-headshot-of-a-confident-fem-1772864652004-ed832249.png", imageAlt: "Michael Chen"},
|
||||||
imageAlt: "Michael Chen",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: "3",
|
id: "3", name: "Emily Rodriguez", role: "Marketing Director", company: "GrowthCo", rating: 5,
|
||||||
name: "Emily Rodriguez",
|
|
||||||
role: "Marketing Director",
|
|
||||||
company: "GrowthCo",
|
|
||||||
rating: 5,
|
|
||||||
imageSrc:
|
imageSrc:
|
||||||
"https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AaqWADaE26HPuu9McAi5WY1Ok3/professional-headshot-of-a-young-profess-1772864652184-6d0f80b0.png",
|
"https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AaqWADaE26HPuu9McAi5WY1Ok3/professional-headshot-of-a-young-profess-1772864652184-6d0f80b0.png", imageAlt: "Emily Rodriguez"},
|
||||||
imageAlt: "Emily Rodriguez",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: "4",
|
id: "4", name: "David Kim", role: "CEO", company: "StartupXYZ", rating: 4,
|
||||||
name: "David Kim",
|
|
||||||
role: "CEO",
|
|
||||||
company: "StartupXYZ",
|
|
||||||
rating: 4,
|
|
||||||
imageSrc:
|
imageSrc:
|
||||||
"https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AaqWADaE26HPuu9McAi5WY1Ok3/professional-headshot-of-a-business-exec-1772864651875-1760e8d0.png",
|
"https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AaqWADaE26HPuu9McAi5WY1Ok3/professional-headshot-of-a-business-exec-1772864651875-1760e8d0.png", imageAlt: "David Kim"},
|
||||||
imageAlt: "David Kim",
|
|
||||||
},
|
|
||||||
]}
|
]}
|
||||||
kpiItems={[
|
kpiItems={[
|
||||||
{ value: "15k+", label: "Happy Customers" },
|
{ value: "15k+", label: "Happy Customers" },
|
||||||
@@ -371,41 +486,23 @@ export default function HomePage() {
|
|||||||
tag="Help"
|
tag="Help"
|
||||||
faqs={[
|
faqs={[
|
||||||
{
|
{
|
||||||
id: "1",
|
id: "1", title: "How do I get access to the templates after purchase?", content:
|
||||||
title: "How do I get access to the templates after purchase?",
|
"After purchase, you'll receive instant access to download all files. No waiting, no delays. All templates are immediately available in your account dashboard."},
|
||||||
content:
|
|
||||||
"After purchase, you'll receive instant access to download all files. No waiting, no delays. All templates are immediately available in your account dashboard.",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: "2",
|
id: "2", title: "Can I use these templates for commercial purposes?", content:
|
||||||
title: "Can I use these templates for commercial purposes?",
|
"Yes! All our templates include commercial rights. You can use them for client projects, resell modified versions, and use them for business purposes with no restrictions."},
|
||||||
content:
|
|
||||||
"Yes! All our templates include commercial rights. You can use them for client projects, resell modified versions, and use them for business purposes with no restrictions.",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: "3",
|
id: "3", title: "What if I'm not satisfied with a template?", content:
|
||||||
title: "What if I'm not satisfied with a template?",
|
"We offer a 30-day money-back guarantee. If you're not completely satisfied, simply request a refund and we'll process it within 24 hours."},
|
||||||
content:
|
|
||||||
"We offer a 30-day money-back guarantee. If you're not completely satisfied, simply request a refund and we'll process it within 24 hours.",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: "4",
|
id: "4", title: "Do you provide updates and support?", content:
|
||||||
title: "Do you provide updates and support?",
|
"Absolutely! All templates receive regular updates with new features and improvements. Our support team is available 24/7 to help you with any questions."},
|
||||||
content:
|
|
||||||
"Absolutely! All templates receive regular updates with new features and improvements. Our support team is available 24/7 to help you with any questions.",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: "5",
|
id: "5", title: "Are the templates compatible with the latest software versions?", content:
|
||||||
title: "Are the templates compatible with the latest software versions?",
|
"Yes, all templates are regularly updated to ensure compatibility with the latest software versions and tools."},
|
||||||
content:
|
|
||||||
"Yes, all templates are regularly updated to ensure compatibility with the latest software versions and tools.",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: "6",
|
id: "6", title: "What payment methods do you accept?", content:
|
||||||
title: "What payment methods do you accept?",
|
"We accept all major credit cards, PayPal, and various international payment methods. Your payment is processed securely and encrypted."},
|
||||||
content:
|
|
||||||
"We accept all major credit cards, PayPal, and various international payment methods. Your payment is processed securely and encrypted.",
|
|
||||||
},
|
|
||||||
]}
|
]}
|
||||||
imageSrc="https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AaqWADaE26HPuu9McAi5WY1Ok3/friendly-minimalist-illustration-of-a-pe-1772864652653-3d5f063c.png?_wi=1"
|
imageSrc="https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AaqWADaE26HPuu9McAi5WY1Ok3/friendly-minimalist-illustration-of-a-pe-1772864652653-3d5f063c.png?_wi=1"
|
||||||
imageAlt="Customer Support Illustration"
|
imageAlt="Customer Support Illustration"
|
||||||
|
|||||||
362
src/components/sections/payment/PaymentTerminal.tsx
Normal file
362
src/components/sections/payment/PaymentTerminal.tsx
Normal file
@@ -0,0 +1,362 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import { CreditCard, Lock, AlertCircle } from "lucide-react";
|
||||||
|
import Input from "@/components/form/Input";
|
||||||
|
|
||||||
|
interface PaymentFormData {
|
||||||
|
cardNumber: string;
|
||||||
|
cardholderName: string;
|
||||||
|
expiryMonth: string;
|
||||||
|
expiryYear: string;
|
||||||
|
cvv: string;
|
||||||
|
billingAddress: string;
|
||||||
|
billingCity: string;
|
||||||
|
billingState: string;
|
||||||
|
billingZip: string;
|
||||||
|
billingCountry: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function PaymentTerminal() {
|
||||||
|
const [formData, setFormData] = useState<PaymentFormData>({
|
||||||
|
cardNumber: "", cardholderName: "", expiryMonth: "", expiryYear: "", cvv: "", billingAddress: "", billingCity: "", billingState: "", billingZip: "", billingCountry: ""});
|
||||||
|
|
||||||
|
const [errors, setErrors] = useState<Record<string, string>>({});
|
||||||
|
const [isProcessing, setIsProcessing] = useState(false);
|
||||||
|
const [successMessage, setSuccessMessage] = useState("");
|
||||||
|
|
||||||
|
const handleInputChange = (field: keyof PaymentFormData, value: string) => {
|
||||||
|
setFormData((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[field]: value,
|
||||||
|
}));
|
||||||
|
// Clear error for this field when user starts typing
|
||||||
|
if (errors[field]) {
|
||||||
|
setErrors((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[field]: ""}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatCardNumber = (value: string) => {
|
||||||
|
const cleaned = value.replace(/\D/g, "");
|
||||||
|
const formatted = cleaned
|
||||||
|
.slice(0, 16)
|
||||||
|
.replace(/\s/g, "")
|
||||||
|
.replace(/(\d{4})/g, "$1 ")
|
||||||
|
.trim();
|
||||||
|
return formatted;
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatCVV = (value: string) => {
|
||||||
|
return value.replace(/\D/g, "").slice(0, 4);
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateForm = (): boolean => {
|
||||||
|
const newErrors: Record<string, string> = {};
|
||||||
|
|
||||||
|
if (!formData.cardNumber || formData.cardNumber.replace(/\s/g, "").length !== 16) {
|
||||||
|
newErrors.cardNumber = "Valid card number required (16 digits)";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!formData.cardholderName.trim()) {
|
||||||
|
newErrors.cardholderName = "Cardholder name required";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!formData.expiryMonth || !formData.expiryYear) {
|
||||||
|
newErrors.expiry = "Expiry date required";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!formData.cvv || formData.cvv.length < 3) {
|
||||||
|
newErrors.cvv = "Valid CVV required (3-4 digits)";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!formData.billingAddress.trim()) {
|
||||||
|
newErrors.billingAddress = "Billing address required";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!formData.billingCity.trim()) {
|
||||||
|
newErrors.billingCity = "City required";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!formData.billingZip.trim()) {
|
||||||
|
newErrors.billingZip = "ZIP code required";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!formData.billingCountry.trim()) {
|
||||||
|
newErrors.billingCountry = "Country required";
|
||||||
|
}
|
||||||
|
|
||||||
|
setErrors(newErrors);
|
||||||
|
return Object.keys(newErrors).length === 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (!validateForm()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsProcessing(true);
|
||||||
|
setSuccessMessage("");
|
||||||
|
|
||||||
|
// Simulate payment processing
|
||||||
|
try {
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||||
|
setSuccessMessage("Payment processed successfully! Your transaction is complete.");
|
||||||
|
// Reset form
|
||||||
|
setFormData({
|
||||||
|
cardNumber: "", cardholderName: "", expiryMonth: "", expiryYear: "", cvv: "", billingAddress: "", billingCity: "", billingState: "", billingZip: "", billingCountry: ""});
|
||||||
|
} catch (error) {
|
||||||
|
setErrors({ submit: "Payment failed. Please try again." });
|
||||||
|
} finally {
|
||||||
|
setIsProcessing(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="py-20 px-6">
|
||||||
|
<div className="max-w-2xl mx-auto">
|
||||||
|
{/* Header */}
|
||||||
|
<div className="text-center mb-12">
|
||||||
|
<div className="flex items-center justify-center mb-4">
|
||||||
|
<CreditCard className="w-8 h-8 text-accent" />
|
||||||
|
</div>
|
||||||
|
<h2 className="text-4xl md:text-5xl font-bold mb-4">Payment Terminal</h2>
|
||||||
|
<p className="text-foreground/70 text-lg">Securely enter your payment information below</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Payment Form */}
|
||||||
|
<div className="bg-card rounded-lg p-8 border border-accent/10">
|
||||||
|
{/* Security Badge */}
|
||||||
|
<div className="flex items-center justify-center gap-2 mb-8 pb-8 border-b border-accent/10">
|
||||||
|
<Lock className="w-4 h-4 text-accent" />
|
||||||
|
<span className="text-sm text-foreground/60">Your payment information is secure and encrypted</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{successMessage && (
|
||||||
|
<div className="mb-6 p-4 bg-green-500/10 border border-green-500/20 rounded-lg flex items-start gap-3">
|
||||||
|
<div className="w-5 h-5 rounded-full bg-green-500 flex items-center justify-center flex-shrink-0 mt-0.5">
|
||||||
|
<span className="text-white text-sm">✓</span>
|
||||||
|
</div>
|
||||||
|
<p className="text-green-600 dark:text-green-400 text-sm">{successMessage}</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<form onSubmit={handleSubmit} className="space-y-6">
|
||||||
|
{/* Card Information */}
|
||||||
|
<div>
|
||||||
|
<h3 className="text-lg font-semibold mb-4">Card Information</h3>
|
||||||
|
<div className="space-y-4">
|
||||||
|
{/* Card Number */}
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium mb-2">Card Number</label>
|
||||||
|
<Input
|
||||||
|
value={formData.cardNumber}
|
||||||
|
onChange={(value) => handleInputChange("cardNumber", formatCardNumber(value))}
|
||||||
|
placeholder="1234 5678 9012 3456"
|
||||||
|
type="text"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
{errors.cardNumber && (
|
||||||
|
<p className="text-red-500 text-xs mt-1 flex items-center gap-1">
|
||||||
|
<AlertCircle className="w-3 h-3" />
|
||||||
|
{errors.cardNumber}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Cardholder Name */}
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium mb-2">Cardholder Name</label>
|
||||||
|
<Input
|
||||||
|
value={formData.cardholderName}
|
||||||
|
onChange={(value) => handleInputChange("cardholderName", value)}
|
||||||
|
placeholder="John Doe"
|
||||||
|
type="text"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
{errors.cardholderName && (
|
||||||
|
<p className="text-red-500 text-xs mt-1 flex items-center gap-1">
|
||||||
|
<AlertCircle className="w-3 h-3" />
|
||||||
|
{errors.cardholderName}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Expiry and CVV */}
|
||||||
|
<div className="grid grid-cols-3 gap-4">
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium mb-2">Month</label>
|
||||||
|
<Input
|
||||||
|
value={formData.expiryMonth}
|
||||||
|
onChange={(value) =>
|
||||||
|
handleInputChange(
|
||||||
|
"expiryMonth", value.replace(/\D/g, "").slice(0, 2)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
placeholder="MM"
|
||||||
|
type="text"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium mb-2">Year</label>
|
||||||
|
<Input
|
||||||
|
value={formData.expiryYear}
|
||||||
|
onChange={(value) =>
|
||||||
|
handleInputChange(
|
||||||
|
"expiryYear", value.replace(/\D/g, "").slice(0, 2)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
placeholder="YY"
|
||||||
|
type="text"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium mb-2">CVV</label>
|
||||||
|
<Input
|
||||||
|
value={formData.cvv}
|
||||||
|
onChange={(value) => handleInputChange("cvv", formatCVV(value))}
|
||||||
|
placeholder="123"
|
||||||
|
type="password"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{errors.expiry && (
|
||||||
|
<p className="text-red-500 text-xs flex items-center gap-1">
|
||||||
|
<AlertCircle className="w-3 h-3" />
|
||||||
|
{errors.expiry}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{errors.cvv && (
|
||||||
|
<p className="text-red-500 text-xs flex items-center gap-1">
|
||||||
|
<AlertCircle className="w-3 h-3" />
|
||||||
|
{errors.cvv}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Billing Address */}
|
||||||
|
<div>
|
||||||
|
<h3 className="text-lg font-semibold mb-4">Billing Address</h3>
|
||||||
|
<div className="space-y-4">
|
||||||
|
{/* Address */}
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium mb-2">Street Address</label>
|
||||||
|
<Input
|
||||||
|
value={formData.billingAddress}
|
||||||
|
onChange={(value) => handleInputChange("billingAddress", value)}
|
||||||
|
placeholder="123 Main Street"
|
||||||
|
type="text"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
{errors.billingAddress && (
|
||||||
|
<p className="text-red-500 text-xs mt-1 flex items-center gap-1">
|
||||||
|
<AlertCircle className="w-3 h-3" />
|
||||||
|
{errors.billingAddress}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* City, State, ZIP */}
|
||||||
|
<div className="grid grid-cols-3 gap-4">
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium mb-2">City</label>
|
||||||
|
<Input
|
||||||
|
value={formData.billingCity}
|
||||||
|
onChange={(value) => handleInputChange("billingCity", value)}
|
||||||
|
placeholder="New York"
|
||||||
|
type="text"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
{errors.billingCity && (
|
||||||
|
<p className="text-red-500 text-xs mt-1 flex items-center gap-1">
|
||||||
|
<AlertCircle className="w-3 h-3" />
|
||||||
|
{errors.billingCity}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium mb-2">State</label>
|
||||||
|
<Input
|
||||||
|
value={formData.billingState}
|
||||||
|
onChange={(value) => handleInputChange("billingState", value)}
|
||||||
|
placeholder="NY"
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium mb-2">ZIP Code</label>
|
||||||
|
<Input
|
||||||
|
value={formData.billingZip}
|
||||||
|
onChange={(value) => handleInputChange("billingZip", value)}
|
||||||
|
placeholder="10001"
|
||||||
|
type="text"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
{errors.billingZip && (
|
||||||
|
<p className="text-red-500 text-xs mt-1 flex items-center gap-1">
|
||||||
|
<AlertCircle className="w-3 h-3" />
|
||||||
|
{errors.billingZip}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Country */}
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium mb-2">Country</label>
|
||||||
|
<Input
|
||||||
|
value={formData.billingCountry}
|
||||||
|
onChange={(value) => handleInputChange("billingCountry", value)}
|
||||||
|
placeholder="United States"
|
||||||
|
type="text"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
{errors.billingCountry && (
|
||||||
|
<p className="text-red-500 text-xs mt-1 flex items-center gap-1">
|
||||||
|
<AlertCircle className="w-3 h-3" />
|
||||||
|
{errors.billingCountry}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Submit Error */}
|
||||||
|
{errors.submit && (
|
||||||
|
<div className="p-4 bg-red-500/10 border border-red-500/20 rounded-lg flex items-start gap-3">
|
||||||
|
<AlertCircle className="w-5 h-5 text-red-500 flex-shrink-0 mt-0.5" />
|
||||||
|
<p className="text-red-600 dark:text-red-400 text-sm">{errors.submit}</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Submit Button */}
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
disabled={isProcessing}
|
||||||
|
className="w-full py-3 px-6 bg-primary-cta hover:bg-primary-cta/90 disabled:bg-primary-cta/50 text-white font-semibold rounded-lg transition-colors duration-200 flex items-center justify-center gap-2"
|
||||||
|
>
|
||||||
|
{isProcessing ? (
|
||||||
|
<>
|
||||||
|
<div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin" />
|
||||||
|
Processing...
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Lock className="w-4 h-4" />
|
||||||
|
Process Payment
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user