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>
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>
- 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>
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>
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>
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>
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>
- 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>
- Change "Lunas" badge to use brand accent color instead of hardcoded green
- Fix "Aktif" badge with white text and border for better contrast
- Update MemberAccess page to fetch from paid orders (webinars now show)
- Remove payment_provider filter completely since only Pakasir is used
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Remove payment_provider filter to show all paid products (webinars now appear)
- Fix webinar event_start field loading in AdminProducts (format to datetime-local)
- Update order status badge colors for better visibility (green for paid, amber for pending)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add [&_p]:my-4 to EditorContent className to ensure paragraphs
rendered from Tiptap editor have proper vertical spacing.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add proper margin (my-4) to paragraphs in Tiptap-rendered content.
This ensures proper spacing between paragraphs when displaying
rich text content in product detail pages and other areas.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Fix webinar duration field reference in Calendar (duration_minutes)
- Calculate and display webinar end times in calendar view
- Fetch webinars for selected date in consulting booking
- Block consulting slots that overlap with webinar times
- Show warning when webinars are scheduled on selected date
- Properly handle webinar time range conflicts
This prevents booking conflicts when users try to schedule
consulting sessions during webinar times.
Example: Webinar 20:15-22:15 blocks consulting slots 20:00-22:30
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Admin Cart Visibility:
- Hide cart icon/badge in mobile header for admin users
- Cart was already hidden in desktop sidebar
- Admins don't need to purchase products
Modal Confirmation Improvements:
- Removed confirmation from AdminOrders detail dialog (view-only)
- Removed confirmation from AdminMembers detail dialog (view-only)
- Kept confirmation on AdminProducts form dialog (has form inputs)
- Kept confirmation on AdminEvents form dialogs (Event and Block forms)
- Kept confirmation on AdminConsulting meet link dialog (has form input)
This prevents annoying confirmations on simple view/close actions while
still protecting users from accidentally closing forms with unsaved data.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Public Header Mobile Menu:
- Added hamburger menu for non-logged-in visitors on mobile
- Desktop shows full navigation, mobile shows slide-out menu with icons
- Cart icon remains visible on mobile alongside hamburger
Tiptap Editor List Formatting:
- Added visual styling for bullet lists (disc markers, padding, spacing)
- Added visual styling for ordered lists (decimal markers, padding, spacing)
- List markers now use primary color for better visibility
Product Content HTML Formatting:
- Enhanced prose styling with proper heading sizes (h1, h2, h3)
- Improved list formatting with proper indentation and markers
- Added blockquote styling with left border and italic text
- Added code and preformatted text styling
- Ensures all formatted content displays properly on product detail pages
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Tiptap Editor Improvements:
- Active toolbar buttons now use primary background (black) instead of accent (gray) for better visibility
- Added visual formatting for headings (h1: 2xl bold, h2: xl bold with proper spacing)
- Added visual styling for blockquotes (left border, italic, muted foreground)
Badge Contrast Fixes:
- Product detail page badges now use primary background (black with white text) instead of secondary/accent (gray)
- Fixed product type badge and "Anda memiliki akses" badge
- Fixed "Rekaman segera tersedia" badge
API Query Fix:
- Fixed consulting_slots 400 error by removing unsupported nested relationship filter
- Changed to filter in JavaScript after fetching data from Supabase
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Prevent accidental data loss by requiring confirmation before closing any admin modal via backdrop click. Applied to all admin pages with dialogs.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add shadow-sm to all mobile row cards for consistency
- Hide Card wrapper on mobile for AdminProducts page
- Add shadows to review cards in AdminReviews
- Applied to AdminMembers, AdminOrders, AdminConsulting,
AdminEvents, AdminProducts, AdminReviews
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Fixed build errors caused by incomplete sed script replacement.
Changed mismatched closing </CardContent> and </Card> tags to </div>
in mobile card layouts across admin pages.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Mobile Card Layout Improvements:
- Remove redundant Card/CardContent wrappers in mobile layouts
- Use simple div with border-2 border-border rounded-lg p-4 space-y-3 bg-card
- This provides wider cards without extra padding from wrapper div
- Applied to all admin pages: AdminProducts, AdminOrders, AdminMembers, AdminConsulting, AdminEvents
Integration Tab Fix:
- Remove redundant Calendar ID display in Alert component
- The Calendar ID is already visible in the input field above
- This Alert was causing horizontal overflow on mobile
- Alert showed 'OAuth configured. Calendar ID: {long_email}@group.calendar.google.com'
- Removing this eliminates the overflow issue
Implemented responsive card layout for mobile devices across all admin pages:
- Desktop (md+): Shows traditional table layout
- Mobile (<md): Shows stacked card layout with better readability
AdminProducts.tsx:
- Mobile cards display title, type, price (with sale badge), status
- Action buttons (edit/delete) in header
AdminOrders.tsx:
- Mobile cards display order ID, email, status badge, total, payment method, date
- View detail button in header
AdminMembers.tsx:
- Mobile cards display name, email, role badge, join date
- Action buttons (detail/toggle admin) at bottom with full width
AdminConsulting.tsx (upcoming & past tabs):
- Mobile cards display date, time, client, category, status, meet link
- Action buttons (link/complete/cancel) stacked at bottom
AdminEvents.tsx (events & availability tabs):
- Mobile cards display title/event type or block type, dates, status, notes
- Action buttons (edit/delete) at bottom
This approach provides much better UX on mobile compared to horizontal scrolling,
especially for complex cells like sale prices with badges and multiple action buttons.
AdminMembers.tsx:
- Wrap table in overflow-x-auto div for horizontal scrolling
- Add whitespace-nowrap to TableHead cells
AdminConsulting.tsx:
- Wrap both tables (upcoming and past) in overflow-x-auto div
- Add whitespace-nowrap to all TableHead cells
- Change stats grid from grid-cols-1 md:grid-cols-4 to grid-cols-2 md:grid-cols-4 for better mobile layout
AdminEvents.tsx:
- Wrap both tables (events and availability) in overflow-x-auto div
- Add whitespace-nowrap to all TableHead cells
- Change dialog form grids from grid-cols-2 to grid-cols-1 md:grid-cols-2
CurriculumEditor.tsx:
- Make curriculum header responsive (flex-col sm:flex-row)
- Make module card headers responsive (stack title and buttons on mobile)
- Make lesson items responsive (stack title and buttons on mobile)
All admin pages are now fully responsive with proper horizontal scrolling for tables on mobile and stacked layouts for forms and button groups.
AdminProducts.tsx:
- Wrap table in overflow-x-auto div for horizontal scrolling
- Add whitespace-nowrap to TableHead cells
- Change form grid from grid-cols-2 to grid-cols-1 md:grid-cols-2
AdminOrders.tsx:
- Wrap table in overflow-x-auto div for horizontal scrolling
- Add whitespace-nowrap to TableHead cells
- Change detail dialog grid from grid-cols-2 to grid-cols-1 sm:grid-cols-2
- Change action buttons from flex to flex-col sm:flex-row for mobile stacking
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Changes:
1. Remove rounded-xl from consulting banner (conflicts with narrow border theme)
2. Make all cards equal height with h-full flex flex-col
3. Simplify consulting card to match product card structure:
- Remove Clock/Calendar feature icons (made card too tall)
- Use line-clamp-2 for description (same as products)
- Add line-clamp-1 to title (same as products)
- Use flex-1 justify-end on CardContent (same as products)
- Keep decorative element and gradient background
4. Remove unused Clock and Calendar imports
Result: All cards in the grid now have equal height and aligned bottoms
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Enhancements:
- Add search bar with real-time filtering
- Add category filter buttons (Semua, Webinar, Bootcamp, etc.)
- Show result count ("Menampilkan X dari Y produk")
- Add clear/reset filters button
- Remove booking button from banner (redundant with card)
- Improve banner styling with gradient and rounded-xl
Consulting card improvements:
- Add decorative background element
- Better description of service
- Add feature icons (Clock, Calendar)
- Change price display from "menit" to "sesi" (more premium)
- Improve button text ("Booking Jadwal")
- Use primary color for price and icons
Product card improvements:
- Use stripHtml() for description instead of dangerouslySetInnerHTML
- Fix spacing: add gap-2 between title and badge, shrink-0 on badge
- Larger price display (text-3xl)
- Add discount percentage badge for sale items
- Color sale price with primary color
- Add Check icon for "in cart" state
- Green background for added items (bg-green-500)
- Items-baseline for better price alignment
- Change grid from lg: to xl: for better responsiveness
Empty states:
- Better empty state with Package icon for no products
- New "no results found" state with Search icon
- Add reset filter button to empty state
Technical:
- Add filteredProducts state and logic
- Add searchQuery and selectedType states
- Add clearFilters function
- Import new icons: Clock, Calendar, Check, Search, X
- Import Input component for search bar
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Changed og:image and twitter:image to use full URL instead of relative path
- Added og:url meta tag for proper social media sharing
- Fixes "not a valid URL" error from social media scrapers
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Update AppLayout to display logo and brand name together in all headers (sidebar, public, mobile)
- Improve favicon update in useBranding to create link element if not exists
- Update opengraph metadata to use local image instead of lovable.dev URL
- Change author/meta to WithDwindi branding
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add console.log to track URL generation
- Use functional setState to avoid stale closure issues
- Log settings state before save
This will help diagnose why URLs are empty in save payload
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Remove profiles.role reference (column doesn't exist)
- Use simplified policies (all authenticated users can modify)
- Drop all existing storage policies before creating new ones to avoid conflicts
- Fix policy already exists error in STORAGE_RLS_FIX.sql
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Issue 1 - Logo/Favicon Upload:
- Add preview before upload (user must confirm)
- Show selected file preview with confirm/cancel buttons
- Fallback to current image if preview cancelled
- File size validation (2MB logo, 1MB favicon)
- Add STORAGE_RLS_FIX.sql for storage policy setup
Issue 2 - Badge Colors:
- Already implemented correctly in all files
- All "Lunas" badges use bg-brand-accent class
- Verified: OrderDetail, MemberOrders, Dashboard, MemberDashboard
Issue 3 - Page Title Error:
- Change .single() to .maybeSingle() in useBranding hook
- Handle error case gracefully with default branding
- Set default title even when platform_settings is empty
- This fixes the "JSON object requested, multiple (or no) rows returned" error
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Features implemented:
1. Expired QRIS order handling with dual-path approach
- Product orders: QR regeneration button
- Consulting orders: Immediate cancellation with slot release
2. Standardized status badge wording to "Pending"
3. Fixed TypeScript error in MemberDashboard
4. Dynamic badge colors from branding settings
5. Dynamic page title from branding settings
6. Logo/favicon file upload with auto-delete
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add qr_string and qr_expires_at to Order interface
- Implement 10-second polling for payment status
- Add countdown timer for QR expiration
- Display QR code inline for pending QRIS payments
- Show "Menunggu pembayaran" with spinner while polling
- Add fallback button for payments without QR
Features:
- QR code rendered with qrcode.react library
- Real-time countdown timer (minutes:seconds)
- Auto-refresh when payment detected
- Clean up polling interval on unmount
- Memoized fetchOrder to prevent excessive re-renders
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Remove PayPal payment option from checkout
- Add qr_string and qr_expires_at columns to orders table
- Update create-payment to store QR string in database
- Update pakasir-webhook to clear QR string after payment
- Simplify Checkout to redirect to order detail page
- Clean up unused imports and components
Flow:
1. User checks out with QRIS (only option)
2. Order created with payment_method='qris'
3. QR string stored in database
4. User redirected to Order Detail page
5. QR code displayed in-app with polling
6. After payment, QR string cleared, access granted
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Rename function to abstract payment provider details
- Add support for both QRIS and PayPal methods
- Update frontend to use generic create-payment function
- Remove provider-specific naming from UI/UX
- Payment provider (Pakasir) is now an implementation detail
Response format:
- QRIS: returns qr_string for in-app display, payment_url as fallback
- PayPal: returns payment_url for redirect
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Create create-pakasir-payment edge function to handle payment creation server-side
- Update ConsultingBooking.tsx to use edge function instead of direct API call
- Update Checkout.tsx to use edge function instead of direct API call
- Add config.toml entry for create-pakasir-payment function
- Removes CORS errors when calling Pakasir API from frontend
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add delete button to AdminOrders dialog with Trash2 and AlertTriangle icons
- Create delete-order edge function to handle deletion requests
- Add database migration for delete_order function with comprehensive cleanup
- Update config.toml to register delete-order edge function
- Deletion sequence: reviews → consulting slots → order items → user access → order
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The create-pakasir-payment edge function doesn't exist.
Instead, call Pakasir API directly from the frontend (same as Checkout page).
Uses VITE_PAKASIR_PROJECT_SLUG and VITE_PAKASIR_API_KEY env vars.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Consulting is a service, not a product. It doesn't have order_items.
- Removed cart integration for consulting bookings
- Now calls create-pakasir-payment edge function directly
- Redirects to payment URL without going through checkout
- Removed useCart dependency
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
BREAKING CHANGE: Complete refactor of payment handling
New Architecture:
1. pakasir-webhook (120 lines -> was 535 lines)
- Only verifies signature and updates order status
- Removed: SMTP, email templates, notification logic
2. Database Trigger (NEW)
- Automatically fires when payment_status = 'paid'
- Calls handle-order-paid edge function
- Works for webhook AND manual admin updates
3. handle-order-paid (NEW edge function)
- Grants user access for products
- Creates Google Meet events for consulting
- Sends notifications via send-email-v2
- Triggers webhooks
Benefits:
- Single Responsibility: Each function has one clear purpose
- Trigger works for both webhook and manual admin actions
- Easier to debug and maintain
- Reusable notification system
Migration required: Run 20241223_payment_trigger.sql
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Member OrderDetail page: Shows consulting slots with date/time and Join Meet button
- Admin Orders dialog: Shows consulting slots with meet link access
- Meet button only visible when payment_status is 'paid'
- Both pages show slot status (confirmed/pending)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>