import React, { useEffect, useState } from 'react'; import { useAuth } from '../context/AuthContext'; import { Button } from '../components/Button'; import { LogOut, User, Settings as SettingsIcon, CreditCard, Layout, Cake, ShieldAlert, Clock, Activity, CheckCircle, XCircle, MessageSquare, ArrowRight, Edit2, Download, FileText, ExternalLink, History, RefreshCw, FileDown, ShieldCheck, Calendar } from 'lucide-react'; import { Link, useNavigate, useSearchParams } from 'react-router-dom'; import { supabase, isSupabaseConfigured } from '../lib/supabaseClient'; import { SettingsModal } from '../components/SettingsModal'; import { FeedbackModal } from '../components/FeedbackModal'; import { Invoice, MaintenanceSubscription } from '../types'; interface UserProfile { id: string; email: string; first_name?: string; last_name?: string; date_of_birth: string; } interface OrderHistoryEntry { id: string; status: string; changed_at: string; } interface Bill { id: string; order_id: string; type: 'advance' | 'final'; file_url: string; created_at: string; } interface UserOrder { id: string; created_at: string; package: string; status: string; amount: string; displayId?: string; details?: any; history?: OrderHistoryEntry[]; bills?: Bill[]; subscription?: MaintenanceSubscription; } export const Dashboard: React.FC = () => { const { user, signOut, isAdmin } = useAuth(); const navigate = useNavigate(); const [searchParams] = useSearchParams(); const [profile, setProfile] = useState(null); const [orders, setOrders] = useState([]); const [invoices, setInvoices] = useState([]); const [loadingOrders, setLoadingOrders] = useState(true); const [showSettings, setShowSettings] = useState(false); const [feedbackModalOpen, setFeedbackModalOpen] = useState(false); const [selectedOrderId, setSelectedOrderId] = useState(null); const [feedbackLoading, setFeedbackLoading] = useState(false); // Check for payment success/cancel query params useEffect(() => { const paymentSuccess = searchParams.get('payment_success'); const orderIdParam = searchParams.get('order_id'); if (paymentSuccess === 'true' && orderIdParam) { // Opcionális: Itt lehetne egy sikeres fizetés modált megjeleníteni vagy toast üzenetet // A státusz frissítést (pl. 'pending_feedback' -> 'in_progress') a webhook végezné ideális esetben, // de kliens oldalon is jelezhetjük a sikert. // Mivel a redirect után újratölt az oldal, a fetchData friss adatokat hoz. // Töröljük a query paramokat, hogy ne maradjanak ott window.history.replaceState({}, '', '#/dashboard'); alert("Sikeres fizetés! Köszönjük."); } }, [searchParams]); const fetchOrderHistory = async (orderId: string) => { if (!isSupabaseConfigured) return []; try { const { data } = await supabase .from('order_status_history') .select('*') .eq('order_id', orderId) .order('changed_at', { ascending: false }); return data || []; } catch (e) { return []; } }; const fetchOrderBills = async (orderId: string) => { if (!isSupabaseConfigured) return []; try { const { data } = await supabase .from('bills') .select('*') .eq('order_id', orderId) .order('created_at', { ascending: false }); return data || []; } catch (e) { return []; } }; const fetchSubscription = async (orderId: string) => { if (!isSupabaseConfigured) return undefined; try { const { data } = await supabase .from('maintenance_subscriptions') .select('*') .eq('order_id', orderId) .maybeSingle(); return data as MaintenanceSubscription | undefined; } catch (e) { return undefined; } } const fetchData = async () => { if (!user) return; setLoadingOrders(true); if (!isSupabaseConfigured) { const meta = user.user_metadata || {}; setProfile({ id: user.id, email: user.email || '', first_name: meta.first_name || '', last_name: meta.last_name || '', date_of_birth: meta.date_of_birth || '1990-01-01' }); setOrders([{ id: 'demo-order-1', created_at: new Date().toISOString(), package: 'Pro Web', status: 'pending_feedback', amount: '350.000 Ft', displayId: 'DEMO-123', details: { demoUrl: 'https://example.com', payment_summary: { total: 350000, advance: 80000, remaining: 270000, currency: 'HUF' } }, history: [{ id: 'h1', status: 'new', changed_at: new Date().toISOString() }], bills: [] }]); setLoadingOrders(false); return; } try { const { data: profileData } = await supabase.from('profiles').select('*').eq('id', user.id).maybeSingle(); if (profileData) setProfile(profileData); const { data: orderData } = await supabase.from('orders').select('*').eq('user_id', user.id).order('created_at', { ascending: false }); if (orderData) { const enrichedOrders = await Promise.all(orderData.map(async (o) => { const history = await fetchOrderHistory(o.id); const bills = await fetchOrderBills(o.id); const sub = await fetchSubscription(o.id); return { ...o, displayId: o.id.substring(0, 8).toUpperCase(), history: history, bills: bills, subscription: sub }; })); setOrders(enrichedOrders as UserOrder[]); } const { data: invoiceData } = await supabase.from('invoices').select('*').eq('user_id', user.id).order('created_at', { ascending: false }); if (invoiceData) setInvoices(invoiceData as Invoice[]); } catch (err) { console.error("Unexpected error fetching data:", err); } finally { setLoadingOrders(false); } }; useEffect(() => { fetchData(); }, [user]); const handleLogout = async () => { await signOut(); navigate('/'); }; const openFeedbackModal = (orderId: string) => { setSelectedOrderId(orderId); setFeedbackModalOpen(true); }; const handleSubmitFeedback = async (feedbackData: any) => { if (!selectedOrderId) return; setFeedbackLoading(true); try { if (!isSupabaseConfigured) { await new Promise(r => setTimeout(r, 1000)); setOrders(prev => prev.map(o => o.id === selectedOrderId ? { ...o, status: 'in_progress' } : o)); setFeedbackModalOpen(false); setFeedbackLoading(false); return; } // 1. Fetch fresh order data to get payment details const { data: currentOrder } = await supabase.from('orders').select('*').eq('id', selectedOrderId).single(); if (!currentOrder) throw new Error("Rendelés nem található"); const isApproved = feedbackData.decision === 'approved'; const paymentSummary = currentOrder.details?.payment_summary; const isStandardPackage = ['Landing Page', 'Pro Web'].includes(currentOrder.package); // Extract the new domain if provided during approval const approvedDomain = feedbackData.approval?.domain; // Construct base updated details const updatedDetails = { ...(currentOrder.details || {}), latestFeedback: feedbackData, feedbackDate: new Date().toISOString(), // If approved domain is provided, update the main domainName field so it's visible in Admin ...(approvedDomain ? { domainName: approvedDomain } : {}) }; // Check if payment is needed: Approved + Standard Package + Not Custom Price + Has Remaining Balance // Note: paymentSummary.remaining > 0 check is implicitly handled by Logic in FeedbackModal (it shows payment button only if true) // But we double check here for security. const needsPayment = isApproved && isStandardPackage && !paymentSummary?.is_custom && (paymentSummary?.remaining > 0); if (needsPayment) { // Initiate Stripe Checkout for Final Payment const { data: checkoutData, error: checkoutError } = await supabase.functions.invoke('create-checkout-session', { body: { order_id: selectedOrderId, package_name: currentOrder.package, payment_type: 'final', customer_email: user?.email } }); if (checkoutError) { console.error("Supabase Invoke Error:", checkoutError); let msg = "Ismeretlen hiba"; if (checkoutError && typeof checkoutError === 'object' && 'message' in checkoutError) { msg = checkoutError.message; } throw new Error(`Fizetési rendszer hiba: ${msg}`); } if (checkoutData?.url) { // Update details with feedback content BEFORE redirecting // This ensures we save the "Approval" and the new domain even if they drop off payment (status remains pending_feedback though) await supabase.from('orders').update({ details: updatedDetails }).eq('id', selectedOrderId); // Redirect to Stripe window.location.href = checkoutData.url; return; // Stop execution to allow redirect } else { throw new Error("Nem sikerült létrehozni a fizetési linket."); } } // If no payment needed (e.g. revision request, or custom price, or enterprise), update status directly const newStatus = 'in_progress'; // Send back to dev in both cases (to finalize or revise) const { error: updateError } = await supabase.from('orders').update({ status: newStatus, details: updatedDetails }).eq('id', selectedOrderId); if (updateError) throw updateError; await supabase.from('order_status_history').insert({ order_id: selectedOrderId, status: newStatus }); await fetchData(); setFeedbackModalOpen(false); alert("Visszajelzés sikeresen elküldve!"); } catch (err: any) { console.error("Feedback submit error:", err); alert("Hiba: " + (err.message || "Ismeretlen hiba történt.")); } finally { setFeedbackLoading(false); } }; const getFullName = () => profile?.last_name && profile?.first_name ? `${profile.last_name} ${profile.first_name}` : user?.email?.split('@')[0] || 'Felhasználó'; const getStatusConfig = (status: string) => { switch (status) { case 'new': return { label: 'Beérkezett', color: 'bg-blue-100 text-blue-800', icon: Clock }; case 'in_progress': case 'progress': return { label: 'Fejlesztés', color: 'bg-yellow-100 text-yellow-800', icon: Activity }; case 'pending_feedback': case 'waiting_feedback': case 'feedback': return { label: 'Visszajelzés', color: 'bg-purple-100 text-purple-800', icon: MessageSquare }; case 'completed': return { label: 'Kész', color: 'bg-green-100 text-green-800', icon: CheckCircle }; case 'cancelled': return { label: 'Törölve', color: 'bg-gray-100 text-gray-500', icon: XCircle }; default: return { label: 'Beérkezett', color: 'bg-gray-100 text-gray-800', icon: Clock }; } }; const selectedOrder = orders.find(o => o.id === selectedOrderId); return (
setShowSettings(false)} userProfile={profile} onUpdate={fetchData} /> setFeedbackModalOpen(false)} onSubmit={handleSubmitFeedback} loading={feedbackLoading} order={selectedOrder} />

Fiók Áttekintése

Üdvözöljük, {getFullName()}!

{/* ORDERS COLUMN */}

Aktív Projektjeim

{loadingOrders ? (
) : orders.length > 0 ? (
{orders.map(order => { const statusConfig = getStatusConfig(order.status); const needsFeedback = ['pending_feedback', 'waiting_feedback', 'feedback'].includes(order.status); return (

{order.package}

ID: {order.displayId}

{needsFeedback && ( <> DEMÓ MEGNYITÁSA )} {!needsFeedback && ( {statusConfig.label} )}
{/* PROJECT TIMELINE */}
Projekt Történet
{order.history && order.history.length > 0 ? ( order.history.map((h, idx) => (
{getStatusConfig(h.status).label} {new Date(h.changed_at).toLocaleString('hu-HU')}
)) ) : (

Nincs korábbi bejegyzés.

)}
{/* BILLS & DOCUMENTS / MAINTENANCE */}
Dokumentumok
{order.bills && order.bills.length > 0 ? ( order.bills.map((bill) => (

{bill.type === 'advance' ? 'Előleg számla' : 'Végszámla'}

{new Date(bill.created_at).toLocaleDateString('hu-HU')}

)) ) : (

Még nincs számla.

)}
{/* Maintenance Subscription Status (If Completed) */} {order.subscription && (
Karbantartás

{order.subscription.status === 'active' ? 'Aktív szolgáltatás' : 'Esedékes díj'}

Fordulónap: {new Date(order.subscription.next_billing_date).toLocaleDateString('hu-HU')}

)}
); })}
) : (

Még nincsenek leadott rendelései.

)}
{/* SETTINGS COLUMN */}

Beállítások

E-mail

{user?.email}

Ügyfélközpont

Bármilyen kérdése van a számlázással vagy a fejlesztéssel kapcsolatban, írjon nekünk közvetlenül.

Kapcsolatfelvétel
); };