import { useEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { supabase } from '@/integrations/supabase/client'; import { useAuth } from '@/hooks/useAuth'; import { AppLayout } from '@/components/AppLayout'; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Badge } from '@/components/ui/badge'; import { Skeleton } from '@/components/ui/skeleton'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from '@/components/ui/dialog'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { toast } from '@/hooks/use-toast'; import { formatIDR } from '@/lib/format'; import { Video, Calendar, Clock, User, Link as LinkIcon, ExternalLink, CheckCircle, XCircle, Loader2 } from 'lucide-react'; import { format, parseISO, isToday, isTomorrow, isPast } from 'date-fns'; import { id } from 'date-fns/locale'; interface ConsultingSlot { id: string; user_id: string; order_id: string; date: string; start_time: string; end_time: string; status: string; topic_category: string; notes: string; meet_link: string | null; created_at: string; profiles?: { name: string; email: string; }; } interface PlatformSettings { integration_n8n_base_url?: string; integration_google_calendar_id?: string; } const statusLabels: Record = { pending_payment: { label: 'Menunggu Pembayaran', variant: 'secondary' }, confirmed: { label: 'Dikonfirmasi', variant: 'default' }, completed: { label: 'Selesai', variant: 'outline' }, cancelled: { label: 'Dibatalkan', variant: 'destructive' }, }; export default function AdminConsulting() { const { user, isAdmin, loading: authLoading } = useAuth(); const navigate = useNavigate(); const [slots, setSlots] = useState([]); const [settings, setSettings] = useState({}); const [loading, setLoading] = useState(true); const [selectedSlot, setSelectedSlot] = useState(null); const [meetLink, setMeetLink] = useState(''); const [dialogOpen, setDialogOpen] = useState(false); const [saving, setSaving] = useState(false); const [creatingMeet, setCreatingMeet] = useState(false); const [activeTab, setActiveTab] = useState('upcoming'); useEffect(() => { if (!authLoading) { if (!user) navigate('/auth'); else if (!isAdmin) navigate('/dashboard'); else { fetchSlots(); fetchSettings(); } } }, [user, isAdmin, authLoading]); const fetchSlots = async () => { const { data, error } = await supabase .from('consulting_slots') .select(` *, profiles:user_id (name, email) `) .order('date', { ascending: false }) .order('start_time', { ascending: true }); if (!error && data) setSlots(data); setLoading(false); }; const fetchSettings = async () => { const { data } = await supabase .from('platform_settings') .select('integration_n8n_base_url, integration_google_calendar_id') .single(); if (data) setSettings(data); }; const openMeetDialog = (slot: ConsultingSlot) => { setSelectedSlot(slot); setMeetLink(slot.meet_link || ''); setDialogOpen(true); }; const saveMeetLink = async () => { if (!selectedSlot) return; setSaving(true); const { error } = await supabase .from('consulting_slots') .update({ meet_link: meetLink }) .eq('id', selectedSlot.id); if (error) { toast({ title: 'Error', description: error.message, variant: 'destructive' }); } else { toast({ title: 'Berhasil', description: 'Link Google Meet disimpan' }); setDialogOpen(false); fetchSlots(); // Send notification to client with meet link if (meetLink && selectedSlot.profiles?.email) { try { await supabase.functions.invoke('send-notification', { body: { template_key: 'consulting_scheduled', recipient_email: selectedSlot.profiles.email, recipient_name: selectedSlot.profiles.name, variables: { consultation_date: format(parseISO(selectedSlot.date), 'd MMMM yyyy', { locale: id }), consultation_time: `${selectedSlot.start_time.substring(0, 5)} - ${selectedSlot.end_time.substring(0, 5)}`, meet_link: meetLink, topic_category: selectedSlot.topic_category, }, }, }); } catch (err) { console.log('Notification skipped:', err); } } } setSaving(false); }; const createMeetLink = async () => { if (!selectedSlot) return; // Check if n8n webhook is configured const webhookUrl = settings.integration_n8n_base_url; if (!webhookUrl) { toast({ title: 'Info', description: 'Webhook URL belum dikonfigurasi di Pengaturan Integrasi. Masukkan link Meet secara manual.', }); return; } setCreatingMeet(true); try { // Call the webhook to create Google Meet link const response = await fetch(`${webhookUrl}/create-meet`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ slot_id: selectedSlot.id, date: selectedSlot.date, start_time: selectedSlot.start_time, end_time: selectedSlot.end_time, topic: selectedSlot.topic_category, client_name: selectedSlot.profiles?.name || 'Client', client_email: selectedSlot.profiles?.email, calendar_id: settings.integration_google_calendar_id, }), }); if (!response.ok) { throw new Error('Webhook request failed'); } const data = await response.json(); if (data.meet_link) { setMeetLink(data.meet_link); toast({ title: 'Berhasil', description: 'Link Google Meet dibuat' }); } else { throw new Error('No meet_link in response'); } } catch (error) { console.error('Error creating meet link:', error); toast({ title: 'Gagal', description: 'Gagal membuat link Meet. Pastikan webhook sudah dikonfigurasi dengan benar.', variant: 'destructive', }); } finally { setCreatingMeet(false); } }; const updateSlotStatus = async (slotId: string, newStatus: string) => { const { error } = await supabase .from('consulting_slots') .update({ status: newStatus }) .eq('id', slotId); if (error) { toast({ title: 'Error', description: error.message, variant: 'destructive' }); } else { toast({ title: 'Berhasil', description: `Status diubah ke ${statusLabels[newStatus]?.label || newStatus}` }); fetchSlots(); } }; if (authLoading || loading) { return (
); } const today = new Date().toISOString().split('T')[0]; const upcomingSlots = slots.filter(s => s.date >= today && (s.status === 'confirmed' || s.status === 'pending_payment')); const pastSlots = slots.filter(s => s.date < today || s.status === 'completed' || s.status === 'cancelled'); const todaySlots = slots.filter(s => isToday(parseISO(s.date)) && s.status === 'confirmed'); return (

Kelola jadwal dan link Google Meet untuk sesi konsultasi

{/* Today's Sessions Alert */} {todaySlots.length > 0 && (

Sesi Hari Ini ({todaySlots.length})

{todaySlots.map(slot => (
{slot.start_time.substring(0, 5)} - {slot.profiles?.name || 'N/A'} ({slot.topic_category}) {slot.meet_link ? ( Join ) : ( )}
))}
)} {/* Stats */}
{todaySlots.length}

Hari Ini

{upcomingSlots.filter(s => s.status === 'confirmed').length}

Dikonfirmasi

{upcomingSlots.filter(s => !s.meet_link && s.status === 'confirmed').length}

Perlu Link Meet

{pastSlots.filter(s => s.status === 'completed').length}

Selesai

{/* Tabs */} Mendatang ({upcomingSlots.length}) Riwayat ({pastSlots.length})
Tanggal Waktu Klien Kategori Status Link Meet Aksi {upcomingSlots.map((slot) => (
{format(parseISO(slot.date), 'd MMM yyyy', { locale: id })} {isToday(parseISO(slot.date)) && Hari Ini} {isTomorrow(parseISO(slot.date)) && Besok}
{slot.start_time.substring(0, 5)} - {slot.end_time.substring(0, 5)}

{slot.profiles?.name || '-'}

{slot.profiles?.email}

{slot.topic_category} {statusLabels[slot.status]?.label || slot.status} {slot.meet_link ? ( Buka ) : ( - )} {slot.status === 'confirmed' && ( <> )}
))} {upcomingSlots.length === 0 && ( Tidak ada jadwal mendatang )}
Tanggal Waktu Klien Kategori Status {pastSlots.slice(0, 20).map((slot) => ( {format(parseISO(slot.date), 'd MMM yyyy', { locale: id })} {slot.start_time.substring(0, 5)} - {slot.end_time.substring(0, 5)} {slot.profiles?.name || '-'} {slot.topic_category} {statusLabels[slot.status]?.label || slot.status} ))} {pastSlots.length === 0 && ( Belum ada riwayat konsultasi )}
{/* Meet Link Dialog */} Link Google Meet Tambahkan atau edit link Google Meet untuk sesi ini
{selectedSlot && (

Tanggal: {format(parseISO(selectedSlot.date), 'd MMMM yyyy', { locale: id })}

Waktu: {selectedSlot.start_time.substring(0, 5)} - {selectedSlot.end_time.substring(0, 5)}

Klien: {selectedSlot.profiles?.name}

Topik: {selectedSlot.topic_category}

{selectedSlot.notes &&

Catatan: {selectedSlot.notes}

}
)}
setMeetLink(e.target.value)} placeholder="https://meet.google.com/xxx-xxxx-xxx" className="border-2" />
{!settings.integration_n8n_base_url && (

Tip: Konfigurasi webhook di Pengaturan → Integrasi untuk pembuatan otomatis

)}
); }