Commit Graph

194 Commits

Author SHA1 Message Date
dwindown
3eb53406c9 Auto-cancel expired consulting orders and prefill re-booking
**Features Implemented:**

1. **Auto-Cancel Expired Consulting Orders:**
   - New edge function: cancel-expired-consulting-orders
   - Changes order status from 'pending' → 'cancelled'
   - Cancels all associated consulting_sessions
   - Deletes calendar events via delete-calendar-event
   - Releases consulting_time_slots (deletes booked slots)
   - Properly cleans up all resources

2. **Smart Re-Booking with Pre-filled Data:**
   - OrderDetail.tsx stores expired order data in sessionStorage:
     - fromExpiredOrder flag
     - Original orderId
     - topicCategory
     - notes
   - ConsultingBooking.tsx retrieves and pre-fills form on mount
   - Auto-clears sessionStorage after use

3. **Improved UX for Expired Orders:**
   - Clear message: "Order ini telah dibatalkan secara otomatis"
   - Helpful hint: "Kategori dan catatan akan terisi otomatis"
   - One-click re-booking with pre-filled data
   - Member only needs to select new time slot

**How It Works:**

Flow:
1. QRIS expires → Order shows expired message
2. Member clicks "Buat Booking Baru"
3. Data stored in sessionStorage (category, notes)
4. Navigates to /consulting
5. Form auto-fills with previous data
6. Member selects new time → Books new session

**Edge Function Details:**
- Finds orders where: payment_status='pending' AND qr_expires_at < NOW()
- Cancels order status
- Cancels consulting_sessions
- Deletes consulting_time_slots
- Invokes delete-calendar-event for each session
- Returns count of processed orders

**To Deploy:**
1. Deploy cancel-expired-consulting-orders edge function
2. Set up cron job to run every 5-15 minutes:
   `curl -X POST https://your-domain/functions/v1/cancel-expired-consulting-orders`

**Benefits:**
 Orders properly cancelled when QR expires
 Time slots released for other users
 Calendar events cleaned up
 Easy re-booking without re-typing data
 Better UX for expired payment situations

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-28 18:13:20 +07:00
dwindown
b88e308b84 Fix consulting booking navigation URL
**Issue:**
- 'Buat Booking Baru' button navigated to wrong URL
- Used '/consulting-booking' but correct route is '/consulting'

**Fix:**
- Changed line 457: navigate("/consulting-booking") → navigate("/consulting")
- Button now correctly navigates to consulting booking page

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-28 18:01:53 +07:00
dwindown
5c20ea16a3 Fix incorrect Calendar usage in OrderDetail.tsx
**Issue:**
- Runtime error: 'Calendar is not defined' on order detail page
- Line 340 and 458 used <Calendar /> instead of <CalendarIcon />

**Root Cause:**
- OrderDetail.tsx imports: `import { Calendar as CalendarIcon } from 'lucide-react'`
- But JSX used: `<Calendar className="w-5 h-5" />` instead of `<CalendarIcon />`
- This referenced an undefined 'Calendar' variable

**Fix:**
- Changed line 340: <Calendar /> → <CalendarIcon />
- Changed line 458: <Calendar /> → <CalendarIcon />

**Context:**
- CalendarIcon is the lucide-react icon component
- Calendar (without Icon suffix) doesn't exist in this file's scope
- Combined with App.tsx fix, this fully resolves the ReferenceError

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-28 17:55:33 +07:00
dwindown
5a53cf3f99 Fix Calendar naming conflict in App.tsx
**Issue:**
- Runtime error: 'Calendar is not defined' on order detail page
- Import collision between Calendar UI component and Calendar page

**Root Cause:**
- App.tsx imported: `import Calendar from './pages/Calendar'`
- ConsultingBooking.tsx imported: `import { Calendar } from '@/components/ui/calendar'`
- Bundler couldn't resolve which 'Calendar' to use
- Resulted in undefined Calendar at runtime

**Fix:**
- Renamed Calendar page import to CalendarPage in App.tsx
- Updated route to use <CalendarPage /> instead of <Calendar />
- Eliminates naming conflict

**Files Changed:**
- src/App.tsx: Lines 18, 62

This resolves the ReferenceError that prevented members from viewing order details.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-28 16:34:27 +07:00
dwindown
9bb922f5aa Integrate TimeSlotPickerModal and calendar event updates
Add availability checking and calendar sync to admin session editing:

**New Features:**
- Admin can now select time slots using visual picker with availability checking
- Time slot picker respects confirmed sessions and excludes current session from conflict check
- Calendar events are automatically updated when session time changes
- consulting_time_slots table is updated when time changes (old slots deleted, new slots created)

**New Component:**
- src/components/admin/TimeSlotPickerModal.tsx
  - Reusable modal for time slot selection
  - Shows visual grid of available time slots
  - Range selection for multi-slot sessions
  - Availability checking against consulting_sessions
  - Supports editing (excludes current session from conflicts)

**Enhanced AdminConsulting.tsx:**
- Replaced simple time inputs with TimeSlotPickerModal
- Added state: timeSlotPickerOpen, editTotalBlocks, editTotalDuration
- Added handleTimeSlotSelect callback
- Enhanced saveMeetLink to:
  - Update consulting_time_slots when time changes
  - Call update-calendar-event edge function
  - Update calendar event time via Google Calendar API
- Button shows selected time with duration and blocks count

**New Edge Function:**
- supabase/functions/update-calendar-event/index.ts
  - Updates existing Google Calendar events when session time changes
  - Uses PATCH method to update event (preserves event_id and history)
  - Handles OAuth token refresh with caching
  - Only updates start/end time (keeps title, description, meet link)

**Flow:**
1. Admin clicks "Edit" on session → Opens dialog
2. Admin clicks time button → Opens TimeSlotPickerModal
3. Admin selects new time → Only shows available slots
4. On save:
   - consulting_sessions updated with new time
   - Old consulting_time_slots deleted
   - New consulting_time_slots created
   - Google Calendar event updated (same event_id)
   - Meet link preserved

**Benefits:**
-  Prevents double-booking with availability checking
-  Visual time slot selection (same UX as booking page)
-  Calendar events stay in sync (no orphaned events)
-  Time slots table properly maintained
-  Meet link and event_id preserved during time changes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-28 16:02:00 +07:00
dwindown
b1bd092eb8 Fix booking summary end time and enhance calendar event management
**Issue 1: Fix end time display in booking summary**
- Now correctly shows start_time + slot_duration instead of just start_time
- Example: 09:30 → 10:00 for 1 slot (30 mins)

**Issue 2: Confirm create-google-meet-event uses consulting_sessions**
- Verified: Function already updates consulting_sessions table
- The data shown is from OLD consulting_slots table (needs migration)

**Issue 3: Delete calendar events when order is deleted**
- Enhanced delete-order function to delete calendar events before removing order
- Calls delete-calendar-event for each session with calendar_event_id

**Issue 4: Admin can now edit session time and manage calendar events**
- Added time editing inputs (start/end time) in admin dialog
- Added "Delete Link & Calendar Event" button to remove meet link
- Shows calendar event connection status (✓ Event Kalender: Terhubung)
- "Regenerate Link" button creates new meet link + calendar event
- Recalculates session duration when time changes

**Issue 5: Enhanced calendar event description**
- Now includes: Kategori, Client email, Catatan, Session ID
- Format: "Kategori: {topic}\n\nClient: {email}\n\nCatatan: {notes}\n\nSession ID: {id}"

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-28 14:30:39 +07:00
dwindown
5ab4e6b974 Add calendar event lifecycle management and "Add to Calendar" feature
- Migrate consulting_slots to consulting_sessions structure
- Add calendar_event_id to track Google Calendar events
- Create delete-calendar-event edge function for auto-cleanup
- Add "Tambah ke Kalender" button for members (OrderDetail, ConsultingHistory)
- Update create-google-meet-event to store calendar event ID
- Update handle-order-paid to use consulting_sessions table
- Remove deprecated create-meet-link function
- Add comprehensive documentation (CALENDAR_INTEGRATION.md, MIGRATION_GUIDE.md)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-28 13:54:16 +07:00
dwindown
952bb209cf Fix mobile bottom navigation wording to match desktop sidebar
Updated mobile navigation labels to be consistent with desktop sidebar:
- User nav: "Home" → "Dashboard", "Kelas" → "Akses", "Pesanan" → "Order"
- Admin nav: "Pengguna" → "Member" (matches sidebar exactly)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-28 01:14:29 +07:00
dwindown
a8341a42ee Fix badge color function name in Dashboard
Changed getStatusColor to getPaymentStatusColor to match the imported helper function from statusHelpers.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-28 01:00:02 +07:00
dwindown
2f198a4d72 Fix consulting slot status label to use 'Pending'
Changed getConsultingSlotStatusLabel to return 'Pending' instead of 'Menunggu Pembayaran' for pending_payment status, making it consistent with payment status labels and more suitable for badge display.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-28 00:55:08 +07:00
dwindown
0a299466d8 Replace dropdown filters with tab buttons across admin pages
Update all admin pages to use tab button filters instead of dropdown selects, following the pattern from MemberAccess.tsx:

Changes:
- AdminProducts: Tab buttons for product type (only bootcamp/webinar shown) and status (active/inactive)
- AdminConsulting: Added status filter with tab buttons (pending payment, confirmed, completed, cancelled)
- AdminOrders: Tab buttons for status filter (all, paid, pending, refunded)
- AdminMembers: Tab buttons for role filter (all, admin, member)
- AdminReviews: Tab buttons for both type (all, consulting, bootcamp, webinar, general) and status (all, pending, approved)

Features added:
- Clear button (X) on search input when text is present
- Reset button appears when any filter is active
- Consistent styling with shadow-sm for active tabs and border-2 for outline tabs
- All filters in vertical stack layout for better mobile responsiveness
- Active state visually distinct with default variant and shadow

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-28 00:33:43 +07:00
dwindown
c993abe1e9 Add search and filter features to admin pages
Implement comprehensive search and filter functionality across all admin dashboard pages:

- AdminProducts: Search by title/description, filter by type/status
- AdminBootcamp: Search by bootcamp title
- AdminConsulting: Search by client name, email, category, or order ID
- AdminOrders: Search by order ID/email, filter by payment status
- AdminMembers: Search by name/email, filter by role (admin/member)
- AdminReviews: Enhanced with search by title, body, reviewer, product; existing filters maintained

Features:
- Consistent UI pattern with search icon and border styling
- Result count display showing filtered vs total items
- Contextual empty state messages
- Responsive grid layout for filters
- Real-time filtering without page reload

Also fix admin page reload redirect race condition where pages would redirect to /dashboard instead of staying on current page after reload. The loading state now properly waits for admin role check to complete.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-28 00:17:53 +07:00
dwindown
690268362a feat: add search to AdminConsulting page
- Add search by client name, email, category, or order ID
- Show result count for filtered data
- Integrate with existing upcoming/past tabs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-28 00:08:21 +07:00
dwindown
3e418759a1 feat: add search and filter to admin pages
- Add search and filter (type, status) to AdminProducts
- Add search to AdminBootcamp
- Change mobile admin nav "Pesanan" to "Order"
- Show result counts for filtered data
- Handle empty states with helpful messages

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-28 00:07:07 +07:00
dwindown
0e3a45cfe2 fix: prevent admin page reload redirect to dashboard
Wait for admin role check to complete before setting loading to false.
This prevents race condition where:
1. authLoading becomes false after session loads
2. isAdmin is still false (async check in progress)
3. Page redirects to /dashboard before isAdmin is set to true

Now loading stays true until BOTH session and admin role are loaded,
ensuring admin pages don't redirect on reload.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-27 23:55:46 +07:00
dwindown
79e1bd82fc fix: consulting slots display and auth reload redirect
- Group consulting slots by date in admin order detail modal
- Show time range from first slot start to last slot end
- Display session count badge for multi-slot orders
- Fix page reload redirecting to main page by ensuring loading state
  is properly synchronized with Supabase session initialization
- Add mounted flag to prevent state updates after unmount

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-27 23:47:53 +07:00
dwindown
777d989d34 feat: improve consulting booking UX - allow single slot selection
- Add pending slot state to distinguish between selected and confirmed slots
- First click: slot shows as pending (amber) with "Pilih" label
- Second click (same slot): confirms single slot selection
- Second click (different slot): creates range from pending to clicked slot
- Fix "Body already consumed" error in OAuth token refresh
- Enhance admin consulting slot display with category and notes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-27 23:40:54 +07:00
dwindown
4d8f66ed3a Fix meet link creation to use edge function instead of n8n webhook
Changes:
1. AdminConsulting.tsx: Update createMeetLink to call Supabase edge function
   - Remove dependency on n8n webhook URL
   - Call create-google-meet-event edge function directly
   - Use environment variables for Supabase URL and anon key
   - Improve error handling and user feedback

2. handle-order-paid: Add comprehensive error logging
   - Log meet response status
   - Log full response data
   - Log errors when meet_link update fails
   - Log error response text when request fails
   - Better debugging for troubleshooting meet creation issues

This fixes:
- CORS issues when calling n8n webhook
- 404 errors from deleted /webhook-test/create-link endpoint
- Manual meet link creation now uses same flow as automatic
- Better visibility into meet creation failures via logs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-27 08:46:55 +07:00
dwindown
47d78cbd98 Fix consulting slots ordering and add debug logging
Changes:
- Sort consulting slots by start_time before processing
- Ensures correct first/last slot selection for calendar events
- Add debug logging to track time slot calculations
- Fixes end time calculation for multi-slot consulting orders

This ensures that when multiple slots are booked:
- Slots are processed in chronological order
- Calendar event uses first slot's start and last slot's end
- Event duration correctly covers all booked slots

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-27 08:27:34 +07:00
dwindown
42d6bd98e2 Fix calendar timezone and group consulting slots by order
Calendar Timezone Fix:
- Add +07:00 timezone offset to date strings in create-google-meet-event
- Fixes 13:00 appearing as 20:00 in Google Calendar
- Now treats times as Asia/Jakarta time explicitly

Single Calendar Event per Order:
- handle-order-paid now creates ONE event for all slots in an order
- Uses first slot's start time and last slot's end time
- Updates all slots with the same meet_link
- Prevents duplicate calendar events for multi-slot orders

Admin Consulting Page Improvements:
- Group consulting slots by order_id
- Display as single row with continuous time range (start-end)
- Show session count when multiple slots (e.g., "2 sesi")
- Consistent with member-facing ConsultingHistory component
- Updated both desktop table and mobile card layouts
- Updated both upcoming and past tabs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-27 01:34:40 +07:00
dwindown
3f0acca658 Fix admin consulting page query
Change from relationship query to manual join to avoid foreign key issues with profiles table

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-27 01:22:15 +07:00
dwindown
17440cdf89 Fix consulting order processing and display
- Fix consulting history to show continuous time range (09:00 - 11:00) instead of listing individual slots
- Add foreign key relationships for consulting_slots (order_id and user_id)
- Fix handle-order-paid to query profiles(email, name) instead of full_name
- Add completed consulting sessions with recordings to Member Access page
- Add user_id foreign key constraint to consulting_slots table
- Add orders foreign key constraint for consulting_slots relationship

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-27 01:17:47 +07:00
dwindown
73c03285ea Debug: Add extensive logging to handle-order-paid
Added detailed logging to diagnose why consulting slots aren't being updated:
- Log order details including consulting_slots count
- Log whether order is detected as consulting order
- Log slot update result and any errors

This will help identify where the process is failing.
2025-12-27 00:07:00 +07:00
dwindown
293d5bd65d Fix: Call handle-order-paid directly from webhook instead of relying on DB trigger
The database trigger approach wasn't working because the trigger either doesn't
exist or the required database settings (app.base_url, app.service_role_key) aren't
configured.

This is a simpler, more reliable solution:
- pakasir-webhook updates order to 'paid' status
- pakasir-webhook then directly calls handle-order-paid edge function
- No dependency on database triggers or settings

This matches how other parts of the system work - direct function calls with
environment variables, not database triggers.
2025-12-26 23:54:13 +07:00
dwindown
390fde9bf2 Fix: Handle consulting orders properly in handle-order-paid edge function
Critical bug fix: Consulting orders were not being processed after payment because
the function checked order_items for consulting products, but consulting orders
don't have order_items - they have consulting_slots instead.

Changes:
- Fetch consulting_slots along with order_items in the query
- Check for consulting_slots.length > 0 to detect consulting orders
- Update consulting_slots status from 'pending_payment' to 'confirmed'
- Create Google Meet events for each consulting slot
- Send consulting_scheduled notification

This fixes the issue where:
- Consulting slots stayed in 'pending_payment' status after payment
- No meet links were generated
- No access was granted
- Schedules didn't show up in admin or member dashboard
2025-12-26 23:25:55 +07:00
dwindown
1743f95000 Fix: Add missing payment_method to consulting order creation
The consulting booking flow was missing payment_method: 'qris' when creating
orders. This caused the OrderDetail page to skip rendering the QR code section
because it checks order.payment_method === 'qris'.

Product orders already had this field, which is why QR codes displayed correctly
for them but not for consulting orders.

The root cause was in ConsultingBooking.tsx line 303-314 where the order insert
was missing the payment_method field that was present in Checkout.tsx.
2025-12-26 22:49:09 +07:00
dwindown
a567b683af Fix consulting order QR display and remove duplicate slots card
1. QR Code Display Fix:
   - Removed qr_string from required condition
   - Added fallback for when QR is still processing
   - Shows payment_url button if QR string not available yet
   - Helps users pay while QR code is being generated

2. Consulting Slots Display Fix:
   - Removed duplicate "Detail Jadwal" card
   - Slots now only shown once in main session card
   - Displays as continuous time range (start to end)
   - Shows total duration (e.g., "3 blok (135 menit)")

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-26 22:24:21 +07:00
dwindown
2dae2fdc33 Fix consulting order detail to show slots and QR code
Fixed isConsultingOrder detection:
- Now checks consultingSlots.length > 0 instead of only checking order_items
- Consulting orders don't have order_items, only consulting_slots

Always fetch consulting slots:
- Removed conditional check that only fetched slots for consulting products
- Now always queries consulting_slots table for any order
- This ensures consulting booking info displays correctly

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-26 21:32:13 +07:00
dwindown
c09d8b0c2a Fix consulting booking flow and export CSV format
1. CSV Export: Use raw numbers for Total and Refund Amount columns
   - Changed from formatted IDR (with dots) to plain numbers
   - Prevents Excel from breaking values with thousand separators

2. Consulting Booking Flow:
   - Fixed "Booking Sekarang" to navigate to order detail instead of redirecting to Pakasir
   - Payment QR code now displays in OrderDetail page
   - Consulting orders show slot details instead of empty items list

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-26 21:09:06 +07:00
dwindown
bf212fb973 Add CSV export functionality for admin orders
- Created exportCSV utility with convertToCSV, downloadCSV, formatExportDate, formatExportIDR
- Added export button to AdminOrders page with loading state
- Export includes all order fields: ID, email, total, status, payment method, date, refund info
- CSV format compatible with Excel and Google Sheets

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-26 18:25:43 +07:00
dwindown
5a05203f2b Update all pages to use centralized status helpers
Changes:
- Update MemberOrders to use getPaymentStatusLabel and getPaymentStatusColor
- Update OrderDetail to use centralized helpers
- Remove duplicate getStatusColor and getStatusLabel functions
- Dashboard.tsx already using imported helpers

Benefits:
- DRY principle - single source of truth
- Consistent Indonesian labels everywhere
- Easy to update status styling in one place

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-26 18:19:23 +07:00
dwindown
d089fcc769 Create centralized status management system
- Add statusHelpers.ts with single source of truth for all status labels/colors
- Update AdminOrders to use centralized helpers
- Add utility functions: canRefundOrder, canCancelOrder, canMarkAsPaid
- Improve consistency across payment status handling

Benefits:
- Consistent Indonesian labels everywhere
- DRY principle - no more duplicate switch statements
- Easy to update status styling in one place
- Reusable across all components

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-26 17:07:56 +07:00
dwindown
81bbafcff0 Add refund system and meet link management
Refund System:
- Add refund processing with amount and reason tracking
- Auto-revoke product access on refund
- Support full and partial refunds
- Add database fields for refund tracking

Meet Link Management:
- Show meet link status badge (Ready/Not Ready)
- Add manual meet link creation/update form
- Allow admin to create meet links if auto-creation fails

Database Migration:
- Add refund_amount, refund_reason, refunded_at, refunded_by to orders
- Add cancellation_reason to orders and consulting_slots

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-26 17:05:25 +07:00
dwindown
b955445dea Add filters to Member Access and Member Orders pages
- Member Access: Add product kind filter pills + search input
- Member Orders: Add order status filter pills with counts
- Both pages show results count and empty state when no results
- Include reset filter button and clear search functionality

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-26 16:10:26 +07:00
dwindown
a824e101ed Use live profile data for reviews instead of frozen reviewer_name
Changes:
- Revert to using profiles!user_id (name, avatar_url) JOIN for reviews
- Remove reviewer_name storage from ReviewModal (no longer needed)
- Add avatar display to ReviewCard component
- Reviews now sync automatically with profile changes
- Public queries safely expose only name + avatar via RLS

This ensures:
- Name/avatar changes update across all reviews automatically
- No frozen/outdated reviewer data
- Only public profile fields exposed (secure)
- Reviews serve as live, credible social proof

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-26 15:39:58 +07:00
dwindown
74b7dd09ea Fix reviewer name priority to use live profile data first
Changed fallback order from:
  reviewer_name || profiles.name || 'Anonymous'
To:
  profiles.name || reviewer_name || 'Anonymous'

This ensures:
1. Live profile name is always shown (current data)
2. Falls back to stored reviewer_name if profile deleted
3. Shows "Anonymous" as last resort

Fixes issue where name changes don't reflect on old reviews

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-26 09:36:54 +07:00
dwindown
9b2ac9beee Hide Quick Access section when no scheduled events available
- Check if quickAccessItems has any items before rendering section
- If no consulting/webinar events qualify, entire section is hidden
- Prevents empty "Akses Cepat" section from showing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-26 01:53:30 +07:00
dwindown
734aa967ac Refactor quick access section to only show scheduled events
Changes:
- Add consulting slots fetching to get confirmed upcoming sessions
- Update getQuickAction logic:
  * Consulting: Only show if has confirmed upcoming slot with meet_link
  * Webinar: Only show if event_start + duration hasn't ended
  * Bootcamp: Removed from quick access (self-paced, not scheduled)
- Filter out items without valid quick actions
- Remove unused Calendar and BookOpen imports

Quick access now truly means "it is scheduled, here's the shortcut to join"

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-26 01:40:52 +07:00
dwindown
91bec42c4b Add missing Badge import to Bootcamp page
Fixes "Badge is not defined" error in bootcamp focus page.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-26 01:14:43 +07:00
dwindown
e512956444 Add celebratory review UI to Bootcamp page & fix access page
Bootcamp page changes:
- Add UserReview interface to store full review data
- Fetch review data with is_approved status
- Add celebratory UI when review is approved:
  - Gradient background with brand accent
  - "Ulasan Anda Terbit!" heading with "Disetujui" badge
  - Display user's review with stars, title, body
  - Publication date
- Show pending state with clock icon while waiting approval
- Update onSuccess callback to refresh review data

MemberAccess page changes:
- Change "Lanjutkan Bootcamp" to "Mulai Bootcamp" (clearer)
- Fix webinar action buttons:
  - Check if event_start has passed
  - Only show "Gabung Webinar" if webinar hasn't ended
  - Show "Tonton Rekaman" button if recording_url exists
  - Show "Rekaman segera tersedia" badge for passed webinars without recording

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-26 01:11:11 +07:00
dwindown
f1fb2758f8 Fix webinar join logic to use event_start + duration
Users can now join webinar even if it's already started:
- isWebinarJoinable(): returns true if current time <= event_start + duration
- isWebinarEnded(): returns true if current time > event_start + duration
- "Gabung Webinar" button shows as long as webinar hasn't ended
- This allows latecomers to join immediately

Previously used only event_start which prevented joining after start time.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-26 00:56:03 +07:00
dwindown
ae2a0bf3a1 Fix bootcamp and webinar action buttons
Bootcamp fixes:
- Change "Sudah Selesai" to "Selesai" (shorter, cleaner)
- Replace "Selanjutnya" button with "Beri Ulasan" when all lessons completed
- Makes more sense than "Lanjutkan" when there's nothing to continue

Webinar fixes:
- Check if webinar has ended based on event_start date
- Only show "Gabung Webinar" button if webinar hasn't ended AND has meeting link
- Show "Rekaman segera tersedia" badge for passed webinars without recording
- Only show recording player/video if recording_url exists

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-26 00:53:54 +07:00
dwindown
ed0d1b0ac8 Add celebratory review display after approval
Changes:
- Fetch user's full review data (not just approval status)
- Show celebratory UI when review is approved:
  - Gradient background with brand accent colors
  - "Ulasan Anda Terbit!" heading with approval badge
  - Display user's review with star rating
  - Thank you message for contributing
- Show pending state with clock icon while waiting approval
- Update review modal to refresh data after submission

This creates a proud moment for users when their review is approved!

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-26 00:41:12 +07:00
dwindown
b4d3b1a580 Remove debug console logs from review components
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-26 00:33:29 +07:00
dwindown
50a642b07b Fix reviewer name display in TestimonialsSection
- Change from INNER JOIN to LEFT JOIN (profiles:user_id → profiles!user_id)
- Add reviewer_name to SELECT clause
- Update fallback logic to prioritize reviewer_name over profiles.name
- Add debug console logging

This fixes the "Anonymous" reviewer name issue on homepage.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-26 00:29:39 +07:00
dwindown
a412baad53 Add enhanced debugging for review API response
Added console logging to track:
- When fetchReviews is called
- Raw API response data
- Any errors from Supabase

This will help debug why reviewer_name is not appearing
in the API response.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-26 00:10:07 +07:00
dwindown
196d3e9211 Fix review name capture with auth metadata fallback
Changes:
- ReviewModal now fetches name from profiles, auth metadata, or email
- Added console logging to debug review data
- Falls back to email username if no name found
- This ensures reviewer_name is always populated

For existing reviews without names, run:
UPDATE reviews r
SET reviewer_name = (
  SELECT COALESCE(
    raw_user_meta_data->>'name',
    SPLIT_PART(email, '@', 1)
  )
  FROM auth.users WHERE id = r.user_id
)
WHERE reviewer_name IS NULL;

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-25 23:35:40 +07:00
dwindown
2dd9d544ee Add webinar recording page with embedded video player
Changes:
- Create WebinarRecording page with embedded video player
- Supports YouTube, Vimeo, Google Drive, and direct MP4
- Check access via user_access or paid orders
- Update webinar recording buttons to navigate to page instead of new tab
- Add route /webinar/:slug

This keeps users on the platform for better UX instead of
redirecting to external video sites.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-25 23:05:32 +07:00
dwindown
e347a780f8 Fix review system: display real names and check approval status
Changes:
- ProductReviews.tsx: Use LEFT JOIN and fetch reviewer_name field
- ReviewModal.tsx: Store reviewer_name at submission time
- ProductDetail.tsx: Check is_approved=true in checkUserReview()
- Add migration for reviewer_name column and approval index

This fixes two issues:
1. Reviews now show real account names instead of "Anonymous"
2. Members no longer see "menunggu moderasi" after approval

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-25 22:29:48 +07:00
dwindown
466cca5cb4 Make all status badges pill-shaped and standardize pending color
- Add rounded-full to all status badges across admin and member pages
- Change Pending badge color from bg-secondary to bg-amber-500 text-white
- Update AdminDashboard to use Badge component instead of inline span
- Standardize badge colors everywhere:
  - Paid (Lunas): bg-brand-accent text-white
  - Pending: bg-amber-500 text-white
  - Cancelled: bg-destructive text-white

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-25 21:23:56 +07:00