mirror of
https://github.com/Motion-Games/MotionWebStudio.git
synced 2026-04-21 17:10:54 +02:00
stripe changes
This commit is contained in:
205
pages/Admin.tsx
205
pages/Admin.tsx
@@ -9,7 +9,8 @@ import {
|
||||
AlertTriangle, Archive, Send, Layout, History, MessageCircle, Info, ChevronDown, ChevronUp,
|
||||
Upload, FileDown, Receipt, CreditCard, DollarSign, Plus, Trash2, Building2, User as UserIcon,
|
||||
Palette, Zap, Lightbulb, Link as LinkIcon, ExternalLink, Target, FileStack, Star, Check,
|
||||
Copy, Cpu, Wand2, Eye, EyeOff, ShieldCheck, Calendar, Search, ArrowUpDown, Filter
|
||||
Copy, Cpu, Wand2, Eye, EyeOff, ShieldCheck, Calendar, Search, ArrowUpDown, Filter,
|
||||
TrendingUp, ArrowRight
|
||||
} from 'lucide-react';
|
||||
import { Button } from '../components/Button';
|
||||
import { supabase, isSupabaseConfigured } from '../lib/supabaseClient';
|
||||
@@ -57,6 +58,7 @@ interface AdminOrder {
|
||||
status: string;
|
||||
date: string;
|
||||
amount: string;
|
||||
created_at: string;
|
||||
details?: any;
|
||||
history?: OrderHistoryEntry[];
|
||||
emailLogs?: EmailLogEntry[];
|
||||
@@ -157,8 +159,8 @@ const getEmailTemplate = (type: string, data: {
|
||||
body: `<div style="${baseStyle}">
|
||||
<div style="${headerStyle}">MotionWeb</div>
|
||||
<p>Kedves <strong>${data.customer}</strong>!</p>
|
||||
<p>Köszönöm a visszajelzését a <strong>${data.package}</strong> demó verziójával kapcsolatban.</p>
|
||||
<p>A kért módosításokat feldolgoztam, és megkezdtem azok átvezetését a weboldalon. Amint elkészültem a frissített verzióval, e-mailben értesítem.</p>
|
||||
<p>Köszönöm a visszajelzéseit! Megkezdtem a kért módosítások átvezetését a weboldalon.</p>
|
||||
<p>Hamarosan jelentkezem a frissített verzióval.</p>
|
||||
<div style="${footerStyle}">Üdvözlettel,<br><strong>Balogh Bence Benedek</strong><br>MotionWeb | motionweb.hu</div>
|
||||
</div>`
|
||||
},
|
||||
@@ -495,9 +497,7 @@ export const Admin: React.FC = () => {
|
||||
try {
|
||||
if (typeof e === 'string') errorMessage = e;
|
||||
else if (e instanceof Error) errorMessage = e.message;
|
||||
else if (typeof e === 'object' && e !== null) {
|
||||
errorMessage = e.message || e.error_description || JSON.stringify(e);
|
||||
}
|
||||
else if (typeof e === 'object') errorMessage = JSON.stringify(e);
|
||||
} catch (jsonErr) {
|
||||
errorMessage = "Nem szerializálható hiba objektum";
|
||||
}
|
||||
@@ -583,22 +583,35 @@ export const Admin: React.FC = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate Stripe Link
|
||||
const { data: checkoutData, error: checkoutError } = await supabase.functions.invoke('create-checkout-session', {
|
||||
body: {
|
||||
order_id: sub.order_id,
|
||||
package_name: 'Maintenance',
|
||||
payment_type: 'annual',
|
||||
customer_email: sub.client_email
|
||||
}
|
||||
});
|
||||
|
||||
if (checkoutError || !checkoutData?.url) {
|
||||
throw new Error('Nem sikerült generálni a fizetési linket.');
|
||||
}
|
||||
|
||||
const paymentLink = checkoutData.url;
|
||||
|
||||
const daysLeft = getDaysRemaining(sub.next_billing_date);
|
||||
const emailType = 'Előfizetés emlékeztető';
|
||||
const nowIso = new Date().toISOString();
|
||||
|
||||
// Placeholder payment link
|
||||
const paymentLink = "https://stripe.com/payment_placeholder";
|
||||
|
||||
const orderData = sub.order as any; // Cast to avoid TS issues if types aren't perfect
|
||||
const customerName = orderData?.customer_name || 'Ügyfél';
|
||||
const customerEmail = sub.client_email || orderData?.customer_email;
|
||||
const domain = orderData?.details?.domainName;
|
||||
const amount = "59 990 Ft"; // Or use orderData.amount if applicable
|
||||
const amount = "59 990 Ft";
|
||||
|
||||
const template = getEmailTemplate(emailType, {
|
||||
customer: customerName,
|
||||
package: 'Fenntartás', // Dummy package name for template type signature
|
||||
package: 'Fenntartás',
|
||||
domain: domain,
|
||||
daysLeft: daysLeft,
|
||||
paymentLink: paymentLink,
|
||||
@@ -927,22 +940,162 @@ export const Admin: React.FC = () => {
|
||||
</div>
|
||||
|
||||
<div className="animate-fade-in">
|
||||
{activeTab === 'overview' && (
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 md:gap-8">
|
||||
<div className="bg-white p-8 rounded-[32px] border border-gray-100 shadow-sm">
|
||||
<p className="text-xs font-bold text-gray-400 uppercase mb-3 tracking-widest">Látogatók</p>
|
||||
<h3 className="text-4xl md:text-5xl font-black text-gray-900">{visitorStats.month}</h3>
|
||||
{activeTab === 'overview' && (() => {
|
||||
// -- Statistics Logic Calculation inside render for Overview Tab --
|
||||
const orderStats = {
|
||||
new: orders.filter(o => o.status === 'new').length,
|
||||
inProgress: orders.filter(o => o.status === 'in_progress').length,
|
||||
feedback: orders.filter(o => ['pending_feedback', 'waiting_feedback'].includes(o.status)).length,
|
||||
completed: orders.filter(o => o.status === 'completed').length
|
||||
};
|
||||
const activeSubs = subscriptions.filter(s => s.status === 'active').length;
|
||||
|
||||
// Generate Yearly Chart Data
|
||||
const currentYear = new Date().getFullYear();
|
||||
const monthlyOrders = Array(12).fill(0);
|
||||
orders.forEach(o => {
|
||||
const d = new Date(o.created_at);
|
||||
if (d.getFullYear() === currentYear) {
|
||||
monthlyOrders[d.getMonth()]++;
|
||||
}
|
||||
});
|
||||
const maxOrderCount = Math.max(...monthlyOrders, 1); // Avoid div by zero
|
||||
const monthLabels = ['Jan', 'Feb', 'Már', 'Ápr', 'Máj', 'Jún', 'Júl', 'Aug', 'Szep', 'Okt', 'Nov', 'Dec'];
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<h3 className="text-xl font-black text-gray-900 tracking-tight">Statisztika & Elemzés</h3>
|
||||
<div className="h-px bg-gray-200 flex-grow"></div>
|
||||
</div>
|
||||
|
||||
{/* Visitors Section */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
<div className="bg-gradient-to-br from-blue-50 to-white p-6 rounded-[28px] border border-blue-100 shadow-sm relative overflow-hidden group">
|
||||
<div className="absolute right-0 top-0 w-24 h-24 bg-blue-100 rounded-full blur-2xl -mr-10 -mt-10 group-hover:bg-blue-200 transition-colors"></div>
|
||||
<div className="relative z-10">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="p-2 bg-blue-100 rounded-xl text-blue-600"><Users className="w-5 h-5" /></div>
|
||||
<span className="text-xs font-black text-blue-800 uppercase tracking-widest">Heti Látogatók</span>
|
||||
</div>
|
||||
<h3 className="text-4xl font-black text-gray-900">{visitorStats.week}</h3>
|
||||
<p className="text-[10px] text-gray-400 font-bold mt-2 uppercase tracking-wide">Aktuális hét</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-gradient-to-br from-purple-50 to-white p-6 rounded-[28px] border border-purple-100 shadow-sm relative overflow-hidden group">
|
||||
<div className="absolute right-0 top-0 w-24 h-24 bg-purple-100 rounded-full blur-2xl -mr-10 -mt-10 group-hover:bg-purple-200 transition-colors"></div>
|
||||
<div className="relative z-10">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="p-2 bg-purple-100 rounded-xl text-purple-600"><Calendar className="w-5 h-5" /></div>
|
||||
<span className="text-xs font-black text-purple-800 uppercase tracking-widest">Havi Látogatók</span>
|
||||
</div>
|
||||
<h3 className="text-4xl font-black text-gray-900">{visitorStats.month}</h3>
|
||||
<p className="text-[10px] text-gray-400 font-bold mt-2 uppercase tracking-wide">Becsült aktivitás</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-gradient-to-br from-gray-50 to-white p-6 rounded-[28px] border border-gray-200 shadow-sm relative overflow-hidden group">
|
||||
<div className="absolute right-0 top-0 w-24 h-24 bg-gray-100 rounded-full blur-2xl -mr-10 -mt-10 group-hover:bg-gray-200 transition-colors"></div>
|
||||
<div className="relative z-10">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="p-2 bg-gray-200 rounded-xl text-gray-600"><Globe className="w-5 h-5" /></div>
|
||||
<span className="text-xs font-black text-gray-600 uppercase tracking-widest">Összes Látogató</span>
|
||||
</div>
|
||||
<h3 className="text-4xl font-black text-gray-900">{visitorStats.month}</h3>
|
||||
<p className="text-[10px] text-gray-400 font-bold mt-2 uppercase tracking-wide">Indulás óta</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Orders Funnel & Active Subs */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-4 gap-6">
|
||||
{/* Funnel */}
|
||||
<div className="lg:col-span-3 bg-white p-8 rounded-[32px] border border-gray-100 shadow-sm">
|
||||
<h4 className="text-sm font-black text-gray-900 uppercase tracking-widest mb-6 flex items-center gap-2">
|
||||
<Activity className="w-4 h-4 text-primary" /> Rendelési Folyamat
|
||||
</h4>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
|
||||
<div className="p-5 rounded-2xl bg-blue-50 border border-blue-100 flex flex-col justify-between h-32 relative overflow-hidden">
|
||||
<div className="absolute right-2 top-2 opacity-10"><ShoppingCart className="w-16 h-16 text-blue-600" /></div>
|
||||
<span className="text-xs font-black text-blue-600 uppercase tracking-widest">Új Rendelés</span>
|
||||
<div className="flex items-end justify-between">
|
||||
<h3 className="text-3xl font-black text-blue-900">{orderStats.new}</h3>
|
||||
<ArrowRight className="w-5 h-5 text-blue-300" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-5 rounded-2xl bg-yellow-50 border border-yellow-100 flex flex-col justify-between h-32 relative overflow-hidden">
|
||||
<div className="absolute right-2 top-2 opacity-10"><RefreshCw className="w-16 h-16 text-yellow-600" /></div>
|
||||
<span className="text-xs font-black text-yellow-600 uppercase tracking-widest">Folyamatban</span>
|
||||
<div className="flex items-end justify-between">
|
||||
<h3 className="text-3xl font-black text-yellow-900">{orderStats.inProgress}</h3>
|
||||
<ArrowRight className="w-5 h-5 text-yellow-300" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-5 rounded-2xl bg-purple-50 border border-purple-100 flex flex-col justify-between h-32 relative overflow-hidden">
|
||||
<div className="absolute right-2 top-2 opacity-10"><MessageSquare className="w-16 h-16 text-purple-600" /></div>
|
||||
<span className="text-xs font-black text-purple-600 uppercase tracking-widest">Visszajelzés</span>
|
||||
<div className="flex items-end justify-between">
|
||||
<h3 className="text-3xl font-black text-purple-900">{orderStats.feedback}</h3>
|
||||
<CheckCircle className="w-5 h-5 text-purple-300" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Active Subs */}
|
||||
<div className="bg-[#0f172a] p-8 rounded-[32px] text-white flex flex-col justify-between relative overflow-hidden shadow-xl">
|
||||
<div className="absolute top-0 right-0 w-32 h-32 bg-primary/20 rounded-full blur-3xl pointer-events-none"></div>
|
||||
<div>
|
||||
<div className="flex items-center gap-2 text-primary mb-4">
|
||||
<ShieldCheck className="w-5 h-5" />
|
||||
<span className="text-xs font-black uppercase tracking-widest">Aktív Előfizetések</span>
|
||||
</div>
|
||||
<h3 className="text-5xl font-black mb-1">{activeSubs}</h3>
|
||||
<p className="text-xs text-gray-400 font-bold uppercase tracking-wide">Karbantartott oldalak</p>
|
||||
</div>
|
||||
<div className="mt-6 pt-6 border-t border-gray-800">
|
||||
<div className="flex justify-between items-center text-xs font-bold text-gray-300">
|
||||
<span>Becsült Bevétel (Havi)</span>
|
||||
<span className="text-primary">{Math.round(activeSubs * (59990/12)).toLocaleString()} Ft</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Yearly Chart */}
|
||||
<div className="bg-white p-8 rounded-[32px] border border-gray-100 shadow-sm">
|
||||
<div className="flex justify-between items-center mb-8">
|
||||
<h4 className="text-sm font-black text-gray-900 uppercase tracking-widest flex items-center gap-2">
|
||||
<TrendingUp className="w-4 h-4 text-primary" /> Rendelések alakulása ({currentYear})
|
||||
</h4>
|
||||
<div className="bg-gray-100 px-3 py-1 rounded-lg text-[10px] font-bold text-gray-500 uppercase">Havi bontás</div>
|
||||
</div>
|
||||
|
||||
<div className="h-64 w-full flex items-end justify-between gap-2 sm:gap-4 px-2">
|
||||
{monthlyOrders.map((count, idx) => {
|
||||
const heightPercentage = maxOrderCount > 0 ? (count / maxOrderCount) * 100 : 0;
|
||||
return (
|
||||
<div key={idx} className="flex-1 flex flex-col items-center gap-2 group">
|
||||
<div className="w-full relative flex items-end justify-center h-48 bg-gray-50 rounded-t-xl rounded-b-md overflow-hidden">
|
||||
<div
|
||||
className="w-full bg-primary/80 group-hover:bg-primary transition-all duration-500 rounded-t-lg relative min-h-[4px]"
|
||||
style={{ height: `${heightPercentage}%` }}
|
||||
>
|
||||
{count > 0 && (
|
||||
<div className="absolute -top-8 left-1/2 -translate-x-1/2 bg-gray-900 text-white text-[10px] font-bold px-2 py-1 rounded opacity-0 group-hover:opacity-100 transition-opacity">
|
||||
{count} db
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<span className="text-[10px] font-bold text-gray-400 uppercase tracking-wide group-hover:text-primary transition-colors">{monthLabels[idx]}</span>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-white p-8 rounded-[32px] border border-gray-100 shadow-sm">
|
||||
<p className="text-xs font-bold text-gray-400 uppercase mb-3 tracking-widest">Rendelések</p>
|
||||
<h3 className="text-4xl md:text-5xl font-black text-gray-900">{orders.length}</h3>
|
||||
</div>
|
||||
<div className="bg-white p-8 rounded-[32px] border border-gray-100 shadow-sm">
|
||||
<p className="text-xs font-bold text-gray-400 uppercase mb-3 tracking-widest">Aktív Előfizetések</p>
|
||||
<h3 className="text-4xl md:text-5xl font-black text-gray-900">{subscriptions.filter(s => s.status === 'active').length}</h3>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
);
|
||||
})()}
|
||||
|
||||
{activeTab === 'users' && (
|
||||
<div className="bg-white rounded-[32px] border border-gray-100 shadow-sm overflow-hidden">
|
||||
|
||||
Reference in New Issue
Block a user