From 21aa5bb5bb9f79c7554db9c04beb3a8ccfc85996 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Wed, 7 Jan 2026 11:58:18 +0000 Subject: [PATCH] Changes --- config/payment.ts | 165 -------- src/components/admin/AnalyticsTab.tsx | 367 ----------------- src/components/admin/DiscountCodesTab.tsx | 456 ---------------------- src/components/admin/ProductsTab.tsx | 378 ------------------ src/components/cart/CartDrawer.tsx | 206 ---------- src/pages/etris.tsx | 15 - 6 files changed, 1587 deletions(-) delete mode 100644 config/payment.ts delete mode 100644 src/components/admin/AnalyticsTab.tsx delete mode 100644 src/components/admin/DiscountCodesTab.tsx delete mode 100644 src/components/admin/ProductsTab.tsx delete mode 100644 src/components/cart/CartDrawer.tsx delete mode 100644 src/pages/etris.tsx diff --git a/config/payment.ts b/config/payment.ts deleted file mode 100644 index 633196a..0000000 --- a/config/payment.ts +++ /dev/null @@ -1,165 +0,0 @@ -/** - * PAYMENT CONFIGURATION - * - * Edit this file to configure all payment methods. - * This is the only file you need to modify for production. - */ - -export const CONFIG = { - // ============================================ - // STORE SETTINGS - // ============================================ - store: { - name: "Secure Market", - currency: "USD", - currencySymbol: "$", - supportEmail: "support@example.com", - pgpKey: "0x1234ABCD5678EFGH", - }, - - // ============================================ - // PRODUCTS - // ============================================ - products: [ - { - id: "prod_001", - name: "Digital Product #001", - description: "Secure digital delivery. Encrypted files with lifetime access.", - priceFiat: 25.00, - priceCrypto: { - btc: "0.001", - eth: "0.015", - ltc: "0.3", - xmr: "0.15", - }, - image: "📦", - downloadUrl: "/downloads/product-001.zip", // After payment verification - }, - { - id: "prod_002", - name: "Premium Bundle", - description: "Complete package with all features and priority support.", - priceFiat: 99.00, - priceCrypto: { - btc: "0.004", - eth: "0.06", - ltc: "1.2", - xmr: "0.6", - }, - image: "💎", - downloadUrl: "/downloads/premium-bundle.zip", - }, - ], - - // ============================================ - // PAYPAL / CARD PAYMENTS - // Get your Client ID: https://developer.paypal.com/dashboard/applications - // ============================================ - paypal: { - enabled: true, - clientId: "YOUR_PAYPAL_CLIENT_ID", // Replace with your PayPal Client ID - currency: "USD", - // PayPal supports: paypal, card, credit, venmo, paylater, bancontact, giropay, ideal, mybank, p24, sepa, sofort - enabledFunding: ["paypal", "card", "venmo", "paylater"], - }, - - // ============================================ - // STRIPE (for Apple Pay / Google Pay) - // Get your keys: https://dashboard.stripe.com/apikeys - // ============================================ - stripe: { - enabled: true, - publishableKey: "YOUR_STRIPE_PUBLISHABLE_KEY", // pk_live_... or pk_test_... - // For server-side (edge function), add STRIPE_SECRET_KEY to your secrets - }, - - // ============================================ - // CRYPTOCURRENCY - // ============================================ - crypto: { - enabled: true, - // Replace with YOUR wallet addresses - wallets: { - btc: { - address: "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", - enabled: true, - }, - eth: { - address: "0x71C7656EC7ab88b098defB751B7401B5f6d8976F", - enabled: true, - }, - ltc: { - address: "ltc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", - enabled: true, - }, - xmr: { - address: "888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H", - enabled: true, - }, - }, - // Auto-detection requires backend (Supabase edge function) - autoDetection: { - enabled: false, - pollIntervalMs: 15000, - supabaseUrl: "https://your-project.supabase.co", - }, - }, - - // ============================================ - // DIRECT PAYMENTS (Manual Verification) - // ============================================ - direct: { - enabled: true, - venmo: { - enabled: true, - username: "@YourVenmoUsername", - link: "https://venmo.com/u/YourVenmoUsername", - }, - cashapp: { - enabled: true, - cashtag: "$YourCashTag", - link: "https://cash.app/$YourCashTag", - }, - zelle: { - enabled: true, - email: "payments@example.com", - phone: "+1234567890", - }, - bankTransfer: { - enabled: true, - bankName: "Your Bank", - accountName: "Your Business Name", - accountNumber: "1234567890", - routingNumber: "021000021", - swift: "YOURSWIFT", - iban: "US00BANK0000001234567890", - }, - giftCards: { - enabled: true, - accepted: ["Amazon", "Steam", "iTunes", "Google Play", "Visa", "Mastercard"], - }, - }, - - // ============================================ - // CAPTCHA SETTINGS - // ============================================ - captcha: { - enabled: true, - maxAttempts: 3, - challengeTypes: ["math", "text", "reverse", "count"], - }, - - // ============================================ - // NOTIFICATIONS (Webhooks) - // Called when payment is submitted for verification - // ============================================ - webhooks: { - onPaymentSubmitted: "", // Optional: https://your-server.com/api/payment-submitted - onPaymentVerified: "", // Optional: https://your-server.com/api/payment-verified - }, -}; - -// Type exports for TypeScript -export type Product = typeof CONFIG.products[0]; -export type CryptoType = keyof typeof CONFIG.crypto.wallets; - diff --git a/src/components/admin/AnalyticsTab.tsx b/src/components/admin/AnalyticsTab.tsx deleted file mode 100644 index 2effd8d..0000000 --- a/src/components/admin/AnalyticsTab.tsx +++ /dev/null @@ -1,367 +0,0 @@ -import { useState, useEffect, useMemo } from "react"; -import { supabase } from "@/integrations/supabase/client"; -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; -import { Badge } from "@/components/ui/badge"; -import { - BarChart, - Bar, - XAxis, - YAxis, - CartesianGrid, - Tooltip, - ResponsiveContainer, - PieChart, - Pie, - Cell, - LineChart, - Line, - Legend, -} from "recharts"; -import { - TrendingUp, - TrendingDown, - DollarSign, - ShoppingCart, - Users, - CreditCard, - Calendar, - RefreshCw, -} from "lucide-react"; -import { CONFIG } from "@/config/payments"; - -interface Order { - id: string; - amount: number; - status: string; - payment_method: string; - created_at: string; - customer_email: string; -} - -const COLORS = ["hsl(142, 76%, 36%)", "hsl(38, 92%, 50%)", "hsl(0, 84%, 60%)", "hsl(240, 4%, 46%)"]; - -const AnalyticsTab = () => { - const [orders, setOrders] = useState([]); - const [loading, setLoading] = useState(true); - const [timeRange, setTimeRange] = useState("30"); - - useEffect(() => { - fetchOrders(); - }, [timeRange]); - - const fetchOrders = async () => { - setLoading(true); - try { - const startDate = new Date(); - startDate.setDate(startDate.getDate() - parseInt(timeRange)); - - const { data, error } = await supabase - .from("orders") - .select("id, amount, status, payment_method, created_at, customer_email") - .gte("created_at", startDate.toISOString()) - .order("created_at", { ascending: true }); - - if (error) throw error; - setOrders(data || []); - } catch (error) { - console.error("Failed to fetch analytics data:", error); - } finally { - setLoading(false); - } - }; - - const stats = useMemo(() => { - const verifiedOrders = orders.filter(o => o.status === "verified" || o.status === "completed"); - const totalRevenue = verifiedOrders.reduce((sum, o) => sum + Number(o.amount), 0); - const avgOrderValue = verifiedOrders.length > 0 ? totalRevenue / verifiedOrders.length : 0; - const uniqueCustomers = new Set(orders.map(o => o.customer_email)).size; - const conversionRate = orders.length > 0 ? (verifiedOrders.length / orders.length) * 100 : 0; - - return { - totalRevenue, - totalOrders: orders.length, - verifiedOrders: verifiedOrders.length, - avgOrderValue, - uniqueCustomers, - conversionRate, - }; - }, [orders]); - - const revenueByDay = useMemo(() => { - const grouped: Record = {}; - - orders.forEach(order => { - if (order.status === "verified" || order.status === "completed") { - const date = new Date(order.created_at).toLocaleDateString("en-US", { - month: "short", - day: "numeric", - }); - if (!grouped[date]) { - grouped[date] = { date, revenue: 0, orders: 0 }; - } - grouped[date].revenue += Number(order.amount); - grouped[date].orders += 1; - } - }); - - return Object.values(grouped); - }, [orders]); - - const paymentMethodData = useMemo(() => { - const grouped: Record = {}; - - orders.forEach(order => { - const method = order.payment_method || "unknown"; - grouped[method] = (grouped[method] || 0) + 1; - }); - - return Object.entries(grouped).map(([name, value]) => ({ - name: name.charAt(0).toUpperCase() + name.slice(1), - value, - })); - }, [orders]); - - const statusData = useMemo(() => { - const grouped: Record = {}; - - orders.forEach(order => { - grouped[order.status] = (grouped[order.status] || 0) + 1; - }); - - return Object.entries(grouped).map(([name, value]) => ({ - name: name.charAt(0).toUpperCase() + name.slice(1), - value, - })); - }, [orders]); - - if (loading) { - return ( -
- - Loading analytics... -
- ); - } - - return ( -
- {/* Time Range Selector */} -
-

- - Analytics Overview -

- -
- - {/* Key Metrics */} -
- - - - Total Revenue - - - - -
- {CONFIG.store.currencySymbol}{stats.totalRevenue.toFixed(2)} -
-

- From {stats.verifiedOrders} verified orders -

-
-
- - - - - Total Orders - - - - -
{stats.totalOrders}
-

- {stats.verifiedOrders} completed -

-
-
- - - - - Avg. Order Value - - - - -
- {CONFIG.store.currencySymbol}{stats.avgOrderValue.toFixed(2)} -
-

- Per verified order -

-
-
- - - - - Conversion Rate - - - - -
{stats.conversionRate.toFixed(1)}%
-

- {stats.uniqueCustomers} unique customers -

-
-
-
- - {/* Charts */} -
- {/* Revenue Over Time */} - - - Revenue Over Time - - - {revenueByDay.length === 0 ? ( -
- No revenue data for this period -
- ) : ( - - - - - `$${value}`} - /> - [`$${value.toFixed(2)}`, "Revenue"]} - /> - - - - )} -
-
- - {/* Orders by Status */} - - - Orders by Status - - - {statusData.length === 0 ? ( -
- No order data for this period -
- ) : ( - - - `${name}: ${value}`} - > - {statusData.map((_, index) => ( - - ))} - - - - - )} -
-
- - {/* Payment Methods */} - - - Orders by Payment Method - - - {paymentMethodData.length === 0 ? ( -
- No payment data for this period -
- ) : ( - - - - - - [value, "Orders"]} - /> - - - - )} -
-
-
-
- ); -}; - -export default AnalyticsTab; diff --git a/src/components/admin/DiscountCodesTab.tsx b/src/components/admin/DiscountCodesTab.tsx deleted file mode 100644 index 9969e89..0000000 --- a/src/components/admin/DiscountCodesTab.tsx +++ /dev/null @@ -1,456 +0,0 @@ -import { useState, useEffect } from "react"; -import { supabase } from "@/integrations/supabase/client"; -import { useToast } from "@/hooks/use-toast"; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import { Switch } from "@/components/ui/switch"; -import { Badge } from "@/components/ui/badge"; -import { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, -} from "@/components/ui/table"; -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, - DialogTrigger, -} from "@/components/ui/dialog"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; -import { Plus, Pencil, Trash2, Ticket, RefreshCw, Copy } from "lucide-react"; - -interface DiscountCode { - id: string; - code: string; - description: string | null; - discount_type: string; - discount_value: number; - min_order_amount: number; - max_uses: number | null; - current_uses: number; - valid_from: string; - valid_until: string | null; - is_active: boolean; - created_at: string; -} - -const DiscountCodesTab = () => { - const { toast } = useToast(); - const [codes, setCodes] = useState([]); - const [loading, setLoading] = useState(true); - const [dialogOpen, setDialogOpen] = useState(false); - const [editingCode, setEditingCode] = useState(null); - const [saving, setSaving] = useState(false); - - const [formData, setFormData] = useState({ - code: "", - description: "", - discount_type: "percentage", - discount_value: "", - min_order_amount: "0", - max_uses: "", - valid_until: "", - is_active: true, - }); - - useEffect(() => { - fetchCodes(); - }, []); - - const fetchCodes = async () => { - setLoading(true); - try { - const { data, error } = await supabase - .from("discount_codes") - .select("*") - .order("created_at", { ascending: false }); - - if (error) throw error; - setCodes(data || []); - } catch (error) { - console.error("Failed to fetch discount codes:", error); - toast({ - title: "Error", - description: "Failed to load discount codes.", - variant: "destructive", - }); - } finally { - setLoading(false); - } - }; - - const resetForm = () => { - setFormData({ - code: "", - description: "", - discount_type: "percentage", - discount_value: "", - min_order_amount: "0", - max_uses: "", - valid_until: "", - is_active: true, - }); - setEditingCode(null); - }; - - const generateCode = () => { - const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; - let code = ""; - for (let i = 0; i < 8; i++) { - code += chars.charAt(Math.floor(Math.random() * chars.length)); - } - setFormData({ ...formData, code }); - }; - - const openEditDialog = (code: DiscountCode) => { - setEditingCode(code); - setFormData({ - code: code.code, - description: code.description || "", - discount_type: code.discount_type, - discount_value: code.discount_value.toString(), - min_order_amount: code.min_order_amount.toString(), - max_uses: code.max_uses?.toString() || "", - valid_until: code.valid_until ? code.valid_until.split("T")[0] : "", - is_active: code.is_active, - }); - setDialogOpen(true); - }; - - const handleSubmit = async () => { - if (!formData.code || !formData.discount_value) { - toast({ - title: "Validation Error", - description: "Code and discount value are required.", - variant: "destructive", - }); - return; - } - - setSaving(true); - try { - const codeData = { - code: formData.code.toUpperCase().trim(), - description: formData.description.trim() || null, - discount_type: formData.discount_type, - discount_value: parseFloat(formData.discount_value), - min_order_amount: parseFloat(formData.min_order_amount) || 0, - max_uses: formData.max_uses ? parseInt(formData.max_uses) : null, - valid_until: formData.valid_until ? new Date(formData.valid_until).toISOString() : null, - is_active: formData.is_active, - }; - - if (editingCode) { - const { error } = await supabase - .from("discount_codes") - .update(codeData) - .eq("id", editingCode.id); - - if (error) throw error; - toast({ title: "Success", description: "Discount code updated." }); - } else { - const { error } = await supabase.from("discount_codes").insert(codeData); - if (error) throw error; - toast({ title: "Success", description: "Discount code created." }); - } - - setDialogOpen(false); - resetForm(); - fetchCodes(); - } catch (error: any) { - console.error("Failed to save discount code:", error); - toast({ - title: "Error", - description: error.message || "Failed to save discount code.", - variant: "destructive", - }); - } finally { - setSaving(false); - } - }; - - const handleDelete = async (id: string) => { - if (!confirm("Are you sure you want to delete this discount code?")) return; - - try { - const { error } = await supabase.from("discount_codes").delete().eq("id", id); - if (error) throw error; - toast({ title: "Success", description: "Discount code deleted." }); - fetchCodes(); - } catch (error) { - console.error("Failed to delete discount code:", error); - toast({ - title: "Error", - description: "Failed to delete discount code.", - variant: "destructive", - }); - } - }; - - const copyCode = (code: string) => { - navigator.clipboard.writeText(code); - toast({ title: "Copied!", description: `Code "${code}" copied to clipboard.` }); - }; - - const toggleActive = async (code: DiscountCode) => { - try { - const { error } = await supabase - .from("discount_codes") - .update({ is_active: !code.is_active }) - .eq("id", code.id); - - if (error) throw error; - fetchCodes(); - } catch (error) { - console.error("Failed to toggle code:", error); - } - }; - - const isExpired = (validUntil: string | null) => { - if (!validUntil) return false; - return new Date(validUntil) < new Date(); - }; - - return ( - - - - - Discount Codes - -
- - { setDialogOpen(open); if (!open) resetForm(); }}> - - - - - - {editingCode ? "Edit Discount Code" : "Create Discount Code"} - - {editingCode ? "Update discount code details." : "Create a new discount code for customers."} - - -
-
- -
- setFormData({ ...formData, code: e.target.value.toUpperCase() })} - placeholder="SAVE20" - className="font-mono" - /> - -
-
-
- - setFormData({ ...formData, description: e.target.value })} - placeholder="Summer sale discount" - /> -
-
-
- - -
-
- - setFormData({ ...formData, discount_value: e.target.value })} - placeholder={formData.discount_type === "percentage" ? "20" : "10.00"} - /> -
-
-
-
- - setFormData({ ...formData, min_order_amount: e.target.value })} - placeholder="0" - /> -
-
- - setFormData({ ...formData, max_uses: e.target.value })} - placeholder="Unlimited" - /> -
-
-
-
- - setFormData({ ...formData, valid_until: e.target.value })} - /> -
-
- setFormData({ ...formData, is_active: checked })} - /> - -
-
-
- - - - -
-
-
-
- - {loading ? ( -
- - Loading... -
- ) : codes.length === 0 ? ( -
- -

No discount codes yet

-
- ) : ( - - - - Code - Discount - Uses - Expires - Status - Actions - - - - {codes.map((code) => ( - - -
- {code.code} - -
-
- - {code.discount_type === "percentage" - ? `${code.discount_value}%` - : `$${Number(code.discount_value).toFixed(2)}`} - - - {code.current_uses}{code.max_uses ? `/${code.max_uses}` : ""} - - - {code.valid_until - ? new Date(code.valid_until).toLocaleDateString() - : "Never"} - - - {isExpired(code.valid_until) ? ( - Expired - ) : ( - toggleActive(code)} - > - {code.is_active ? "Active" : "Inactive"} - - )} - - -
- - -
-
-
- ))} -
-
- )} -
-
- ); -}; - -export default DiscountCodesTab; diff --git a/src/components/admin/ProductsTab.tsx b/src/components/admin/ProductsTab.tsx deleted file mode 100644 index 747a8e6..0000000 --- a/src/components/admin/ProductsTab.tsx +++ /dev/null @@ -1,378 +0,0 @@ -import { useState, useEffect } from "react"; -import { supabase } from "@/integrations/supabase/client"; -import { useToast } from "@/hooks/use-toast"; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import { Textarea } from "@/components/ui/textarea"; -import { Switch } from "@/components/ui/switch"; -import { Badge } from "@/components/ui/badge"; -import { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, -} from "@/components/ui/table"; -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, - DialogTrigger, -} from "@/components/ui/dialog"; -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; -import { Plus, Pencil, Trash2, Package, RefreshCw } from "lucide-react"; - -interface Product { - id: string; - sku: string; - name: string; - description: string | null; - price: number; - currency: string; - download_url: string | null; - is_active: boolean; - stock_quantity: number | null; - created_at: string; -} - -const ProductsTab = () => { - const { toast } = useToast(); - const [products, setProducts] = useState([]); - const [loading, setLoading] = useState(true); - const [dialogOpen, setDialogOpen] = useState(false); - const [editingProduct, setEditingProduct] = useState(null); - const [saving, setSaving] = useState(false); - - const [formData, setFormData] = useState({ - sku: "", - name: "", - description: "", - price: "", - currency: "USD", - download_url: "", - is_active: true, - stock_quantity: "", - }); - - useEffect(() => { - fetchProducts(); - }, []); - - const fetchProducts = async () => { - setLoading(true); - try { - const { data, error } = await supabase - .from("products") - .select("*") - .order("created_at", { ascending: false }); - - if (error) throw error; - setProducts(data || []); - } catch (error) { - console.error("Failed to fetch products:", error); - toast({ - title: "Error", - description: "Failed to load products.", - variant: "destructive", - }); - } finally { - setLoading(false); - } - }; - - const resetForm = () => { - setFormData({ - sku: "", - name: "", - description: "", - price: "", - currency: "USD", - download_url: "", - is_active: true, - stock_quantity: "", - }); - setEditingProduct(null); - }; - - const openEditDialog = (product: Product) => { - setEditingProduct(product); - setFormData({ - sku: product.sku, - name: product.name, - description: product.description || "", - price: product.price.toString(), - currency: product.currency, - download_url: product.download_url || "", - is_active: product.is_active, - stock_quantity: product.stock_quantity?.toString() || "", - }); - setDialogOpen(true); - }; - - const handleSubmit = async () => { - if (!formData.sku || !formData.name || !formData.price) { - toast({ - title: "Validation Error", - description: "SKU, name, and price are required.", - variant: "destructive", - }); - return; - } - - setSaving(true); - try { - const productData = { - sku: formData.sku.trim(), - name: formData.name.trim(), - description: formData.description.trim() || null, - price: parseFloat(formData.price), - currency: formData.currency, - download_url: formData.download_url.trim() || null, - is_active: formData.is_active, - stock_quantity: formData.stock_quantity ? parseInt(formData.stock_quantity) : null, - }; - - if (editingProduct) { - const { error } = await supabase - .from("products") - .update(productData) - .eq("id", editingProduct.id); - - if (error) throw error; - toast({ title: "Success", description: "Product updated." }); - } else { - const { error } = await supabase.from("products").insert(productData); - if (error) throw error; - toast({ title: "Success", description: "Product created." }); - } - - setDialogOpen(false); - resetForm(); - fetchProducts(); - } catch (error: any) { - console.error("Failed to save product:", error); - toast({ - title: "Error", - description: error.message || "Failed to save product.", - variant: "destructive", - }); - } finally { - setSaving(false); - } - }; - - const handleDelete = async (id: string) => { - if (!confirm("Are you sure you want to delete this product?")) return; - - try { - const { error } = await supabase.from("products").delete().eq("id", id); - if (error) throw error; - toast({ title: "Success", description: "Product deleted." }); - fetchProducts(); - } catch (error) { - console.error("Failed to delete product:", error); - toast({ - title: "Error", - description: "Failed to delete product.", - variant: "destructive", - }); - } - }; - - const toggleActive = async (product: Product) => { - try { - const { error } = await supabase - .from("products") - .update({ is_active: !product.is_active }) - .eq("id", product.id); - - if (error) throw error; - fetchProducts(); - } catch (error) { - console.error("Failed to toggle product:", error); - } - }; - - return ( - - - - - Products - -
- - { setDialogOpen(open); if (!open) resetForm(); }}> - - - - - - {editingProduct ? "Edit Product" : "Add Product"} - - {editingProduct ? "Update product details." : "Create a new product."} - - -
-
-
- - setFormData({ ...formData, sku: e.target.value })} - placeholder="prod_001" - /> -
-
- - setFormData({ ...formData, price: e.target.value })} - placeholder="49.99" - /> -
-
-
- - setFormData({ ...formData, name: e.target.value })} - placeholder="Product Name" - /> -
-
- -