'use client'; import { useEffect, useState } from 'react'; import { motion } from 'framer-motion'; import { FaDownload, FaSearch, FaCheck, FaTimes, FaClock, FaTelegramPlane, FaSave } from 'react-icons/fa'; import { useAlbums } from '@/lib/AlbumsContext'; import { Purchase } from '@/lib/types'; import { formatPrice } from '@/lib/utils'; import AdminLayout from '@/components/AdminLayout'; export default function AdminPurchasesPage() { const { albums } = useAlbums(); const [purchases, setPurchases] = useState([]); const [searchTerm, setSearchTerm] = useState(''); const [filterStatus, setFilterStatus] = useState<'all' | 'pending' | 'approved' | 'rejected'>('all'); const [loading, setLoading] = useState(false); const [telegramIds, setTelegramIds] = useState(''); const [telegramBaseUrl, setTelegramBaseUrl] = useState('https://api.telegram.org'); const [telegramBotToken, setTelegramBotToken] = useState(''); const [telegramGreeting, setTelegramGreeting] = useState('Welcome! Choose albums from the catalog below.'); const [telegramCardNumber, setTelegramCardNumber] = useState(''); const [telegramCardHolder, setTelegramCardHolder] = useState(''); const [telegramConfigured, setTelegramConfigured] = useState(false); const [telegramWebhookSecretConfigured, setTelegramWebhookSecretConfigured] = useState(false); const [telegramSaving, setTelegramSaving] = useState(false); const [telegramRegistering, setTelegramRegistering] = useState(false); const [telegramMessage, setTelegramMessage] = useState(''); const fetchPurchases = async (showLoading = true) => { if (showLoading) setLoading(true); try { const response = await fetch('/api/purchases'); if (response.ok) { const data = await response.json(); setPurchases(data.reverse()); } } catch (error) { console.error('Error fetching purchases:', error); } finally { if (showLoading) setLoading(false); } }; useEffect(() => { fetchPurchases(); const refreshTimer = window.setInterval(() => fetchPurchases(false), 10_000); fetch('/api/telegram/settings') .then(async (response) => { if (!response.ok) throw new Error('Failed to load Telegram settings'); return response.json(); }) .then((data) => { setTelegramIds(data.approvers.map((item: { userId: string }) => item.userId).join(', ')); setTelegramBaseUrl(data.baseUrl || 'https://api.telegram.org'); setTelegramGreeting(data.greetingText || 'Welcome! Choose albums from the catalog below.'); setTelegramCardNumber(data.cardNumber || ''); setTelegramCardHolder(data.cardHolderName || ''); setTelegramConfigured(Boolean(data.botConfigured)); setTelegramWebhookSecretConfigured(Boolean(data.webhookSecretConfigured)); }) .catch((error) => { console.error('Error fetching Telegram settings:', error); setTelegramMessage('Could not load Telegram settings.'); }); return () => window.clearInterval(refreshTimer); }, []); const saveTelegramSettings = async () => { const ids = telegramIds .split(/[\s,]+/) .map((id) => id.trim()) .filter(Boolean); if (ids.some((id) => !/^\d+$/.test(id))) { setTelegramMessage('Telegram user IDs must contain digits only.'); return; } setTelegramSaving(true); setTelegramMessage(''); try { const response = await fetch('/api/telegram/settings', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ approvers: ids.map((userId) => ({ userId })), baseUrl: telegramBaseUrl, greetingText: telegramGreeting, cardNumber: telegramCardNumber, cardHolderName: telegramCardHolder, ...(telegramBotToken.trim() ? { botToken: telegramBotToken.trim() } : {}), }), }); const data = await response.json(); if (!response.ok) throw new Error(data.error || 'Failed to save Telegram approvers'); setTelegramIds(data.approvers.map((item: { userId: string }) => item.userId).join(', ')); setTelegramBaseUrl(data.baseUrl); setTelegramGreeting(data.greetingText); setTelegramCardNumber(data.cardNumber); setTelegramCardHolder(data.cardHolderName); setTelegramBotToken(''); setTelegramConfigured(Boolean(data.botConfigured)); setTelegramWebhookSecretConfigured(Boolean(data.webhookSecretConfigured)); setTelegramMessage('Telegram settings saved.'); } catch (error) { setTelegramMessage(error instanceof Error ? error.message : 'Failed to save Telegram approvers.'); } finally { setTelegramSaving(false); } }; const registerTelegramWebhook = async () => { setTelegramRegistering(true); setTelegramMessage(''); try { const response = await fetch('/api/telegram/settings', { method: 'POST' }); const data = await response.json(); if (!response.ok) throw new Error(data.error || 'Failed to register Telegram webhook'); setTelegramMessage(data.message); } catch (error) { setTelegramMessage(error instanceof Error ? error.message : 'Failed to register Telegram webhook.'); } finally { setTelegramRegistering(false); } }; const handleApprove = async (purchaseId: number) => { try { const response = await fetch(`/api/purchases/${purchaseId}/approve`, { method: 'PATCH', }); if (response.ok) { fetchPurchases(); } } catch (error) { console.error('Error approving purchase:', error); alert('Failed to approve purchase'); } }; const handleReject = async (purchaseId: number) => { try { const response = await fetch(`/api/purchases/${purchaseId}/reject`, { method: 'PATCH', }); if (response.ok) { fetchPurchases(); } } catch (error) { console.error('Error rejecting purchase:', error); alert('Failed to reject purchase'); } }; const filteredPurchases = purchases.filter((purchase) => { const purchaseAlbumIds = purchase.albumIds || [purchase.albumId]; const purchaseAlbums = albums.filter((album) => purchaseAlbumIds.includes(album.id)); const matchesSearch = purchaseAlbums.some((album) => album.title.toLowerCase().includes(searchTerm.toLowerCase())) || purchase.transactionId.toLowerCase().includes(searchTerm.toLowerCase()) || purchase.customerName?.toLowerCase().includes(searchTerm.toLowerCase()) || purchase.email?.toLowerCase().includes(searchTerm.toLowerCase()); const matchesStatus = filterStatus === 'all' || purchase.approvalStatus === filterStatus; return matchesSearch && matchesStatus; }); const pendingPurchases = purchases.filter(p => p.approvalStatus === 'pending'); const approvedPurchases = purchases.filter(p => p.approvalStatus === 'approved'); const totalRevenue = approvedPurchases.reduce((total, purchase) => { const purchaseAlbumIds = purchase.albumIds || [purchase.albumId]; return total + albums .filter((album) => purchaseAlbumIds.includes(album.id)) .reduce((subtotal, album) => subtotal + album.price, 0); }, 0); const exportToCSV = () => { const headers = ['Date', 'Time', 'Transaction ID', 'Album', 'Price', 'Customer', 'Email', 'Phone', 'Status', 'Payment Method']; const rows = purchases.map((purchase) => { const purchaseAlbumIds = purchase.albumIds || [purchase.albumId]; const purchaseAlbums = albums.filter((album) => purchaseAlbumIds.includes(album.id)); const date = new Date(purchase.purchaseDate); return [ date.toLocaleDateString(), date.toLocaleTimeString(), purchase.transactionId, purchaseAlbums.map((album) => album.title).join(' + ') || 'Unknown', purchaseAlbums.reduce((total, album) => total + album.price, 0), purchase.customerName || '', purchase.email || '', purchase.phoneNumber || '', purchase.approvalStatus || 'pending', purchase.paymentMethod || 'card-to-card', ]; }); const csvContent = [headers, ...rows].map((row) => row.join(',')).join('\n'); const blob = new Blob([csvContent], { type: 'text/csv' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `purchases-${new Date().toISOString().split('T')[0]}.csv`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); }; const getStatusBadge = (status?: string) => { const statusLower = (status || 'pending').toLowerCase(); if (statusLower === 'approved') { return ( Approved ); } else if (statusLower === 'rejected') { return ( Rejected ); } else { return ( Pending ); } }; return (
{/* Header */}

Purchase Management

{purchases.length} total • {pendingPurchases.length} pending • {formatPrice(totalRevenue)} revenue

Export CSV
{telegramConfigured ? 'Bot configured' : 'Bot token missing'}
setTelegramBaseUrl(event.target.value)} placeholder="https://api.telegram.org" className="w-full px-4 py-3 bg-paper-light border-2 border-paper-brown focus:border-paper-dark focus:outline-none text-paper-dark placeholder-paper-gray shadow-paper" />
setTelegramBotToken(event.target.value)} placeholder={telegramConfigured ? 'Saved — leave blank to keep it' : 'Paste the BotFather token'} autoComplete="new-password" className="w-full px-4 py-3 bg-paper-light border-2 border-paper-brown focus:border-paper-dark focus:outline-none text-paper-dark placeholder-paper-gray shadow-paper" />