diff --git a/src/pages/member/MemberDashboard.tsx b/src/pages/member/MemberDashboard.tsx index 2d29486..a1e3b8a 100644 --- a/src/pages/member/MemberDashboard.tsx +++ b/src/pages/member/MemberDashboard.tsx @@ -8,7 +8,7 @@ import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Skeleton } from "@/components/ui/skeleton"; import { formatIDR } from "@/lib/format"; -import { Video, Calendar, BookOpen, ArrowRight, Package, Receipt, ShoppingBag } from "lucide-react"; +import { Video, ArrowRight, Package, Receipt, ShoppingBag } from "lucide-react"; import { WhatsAppBanner } from "@/components/WhatsAppBanner"; import { ConsultingHistory } from "@/components/reviews/ConsultingHistory"; import { UnpaidOrderAlert } from "@/components/UnpaidOrderAlert"; @@ -22,6 +22,8 @@ interface UserAccess { type: string; meeting_link: string | null; recording_url: string | null; + event_start: string | null; + duration_minutes: number | null; }; } @@ -37,12 +39,23 @@ interface UnpaidConsultingOrder { qr_expires_at: string; } +interface ConsultingSlot { + id: string; + date: string; + start_time: string; + end_time: string; + status: string; + product_id: string | null; + meet_link: string | null; +} + export default function MemberDashboard() { const { user, loading: authLoading } = useAuth(); const navigate = useNavigate(); const [access, setAccess] = useState([]); const [recentOrders, setRecentOrders] = useState([]); const [unpaidConsultingOrders, setUnpaidConsultingOrders] = useState([]); + const [consultingSlots, setConsultingSlots] = useState([]); const [loading, setLoading] = useState(true); const [hasWhatsApp, setHasWhatsApp] = useState(true); @@ -109,10 +122,10 @@ export default function MemberDashboard() { }, []); const fetchData = async () => { - const [accessRes, ordersRes, paidOrdersRes, profileRes] = await Promise.all([ + const [accessRes, ordersRes, paidOrdersRes, profileRes, slotsRes] = await Promise.all([ supabase .from("user_access") - .select(`id, product:products (id, title, slug, type, meeting_link, recording_url)`) + .select(`id, product:products (id, title, slug, type, meeting_link, recording_url, event_start, duration_minutes)`) .eq("user_id", user!.id), supabase.from("orders").select("*").eq("user_id", user!.id).order("created_at", { ascending: false }).limit(3), // Also get products from paid orders (via order_items) @@ -121,13 +134,20 @@ export default function MemberDashboard() { .select( ` order_items ( - product:products (id, title, slug, type, meeting_link, recording_url) + product:products (id, title, slug, type, meeting_link, recording_url, event_start, duration_minutes) ) `, ) .eq("user_id", user!.id) .eq("payment_status", "paid"), supabase.from("profiles").select("whatsapp_number").eq("id", user!.id).single(), + // Fetch confirmed consulting slots for quick access + supabase + .from("consulting_slots") + .select("id, date, start_time, end_time, status, product_id, meet_link") + .eq("user_id", user!.id) + .eq("status", "confirmed") + .order("date", { ascending: false }), ]); // Combine access from user_access and paid orders @@ -149,20 +169,45 @@ export default function MemberDashboard() { setAccess([...directAccess, ...paidProductAccess]); if (ordersRes.data) setRecentOrders(ordersRes.data); if (profileRes.data) setHasWhatsApp(!!profileRes.data.whatsapp_number); + if (slotsRes.data) setConsultingSlots(slotsRes.data as unknown as ConsultingSlot[]); setLoading(false); }; const getQuickAction = (item: UserAccess) => { + const now = new Date(); + switch (item.product.type) { - case "consulting": - return { label: "Jadwalkan", icon: Calendar, href: item.product.meeting_link }; - case "webinar": - if (item.product.recording_url) { - return { label: "Tonton", icon: Video, route: `/webinar/${item.product.slug}` }; + case "consulting": { + // Only show if user has a confirmed upcoming consulting slot for this product + const upcomingSlot = consultingSlots.find( + (slot) => + slot.product_id === item.product.id && + slot.status === "confirmed" && + new Date(slot.date) >= new Date(now.setHours(0, 0, 0, 0)) + ); + + if (upcomingSlot && upcomingSlot.meet_link) { + return { label: "Gabung Konsultasi", icon: Video, href: upcomingSlot.meet_link }; } - return { label: "Gabung", icon: Video, href: item.product.meeting_link }; + return null; + } + case "webinar": { + // Only show if webinar is joinable (hasn't ended yet) + if (!item.product.event_start) return null; + + const eventStart = new Date(item.product.event_start); + const durationMs = (item.product.duration_minutes || 60) * 60 * 1000; + const eventEnd = new Date(eventStart.getTime() + durationMs); + + // Only show if webinar hasn't ended + if (now <= eventEnd) { + return { label: "Gabung Webinar", icon: Video, href: item.product.meeting_link }; + } + return null; + } case "bootcamp": - return { label: "Lanjutkan", icon: BookOpen, route: `/bootcamp/${item.product.slug}` }; + // Don't show bootcamp in quick access - it's self-paced, not scheduled + return null; default: return null; } @@ -250,33 +295,28 @@ export default function MemberDashboard() {
- {access.slice(0, 3).map((item) => { - const action = getQuickAction(item); - return ( + {access + .map((item) => ({ item, action: getQuickAction(item) })) + .filter(({ action }) => action !== null) + .slice(0, 3) + .map(({ item, action }) => ( {item.product.title} {item.product.type} - {action && - (action.route ? ( - - ) : action.href ? ( - - ) : null)} + + + )} - ); - })} + ))}
)}