Commit Graph

290 Commits

Author SHA1 Message Date
dwindown
52b16dce07 Implement collaboration wallets, withdrawals, and app UI flows 2026-02-03 16:03:11 +07:00
dwindown
8e64780f72 Ensure product titles have minimum 2-line height
Add min-h-[2.5rem] to product titles for consistent card layout
across all products, regardless of title length.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-02 19:05:23 +07:00
dwindown
da84d0e44d Fix TypeScript errors in Products page
- Add explicit type annotation for productTypes array
- Add type assertion for product.type in Set conversion
- Add React import to resolve module warnings
- Remove unused consulting availability banner
- Improve type safety for onChange handlers

Resolves IDE TypeScript warnings caused by missing type annotations.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-02 18:16:12 +07:00
dwindown
f3117308c3 Add error handling for Supabase auth initialization to handle CORS errors gracefully
When a user visits the site while logged out, Supabase may still try to refresh
stale tokens from localStorage. If CORS is not properly configured, this causes
the error shown in console and the app to get stuck.

Added error handling to catch these initialization errors and set loading to false
so the app can load properly even when auth refresh fails.

Note: The proper fix is to add the origin to Supabase CORS settings:
https://supabase.com/dashboard/project/lovable-backoffice/settings/api

This just prevents the app from hanging when such errors occur.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-02 13:22:49 +07:00
dwindown
d47be3aca6 Fix WebinarRecording access check to support M3U8 and MP4
The access check was only checking recording_url and rejecting access when null,
even if m3u8_url or mp4_url existed. Now checks all recording types.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-17 11:54:22 +07:00
dwindown
221ae195e9 Fix ProductDetail webinar recording detection to support M3U8 and MP4
Similar to MemberAccess.tsx, ProductDetail.tsx was only checking recording_url
and ignoring M3U8 and MP4 recordings from Adilo.

Added hasRecording() helper that checks all recording types and updated:
- renderActionButtons webinar card display
- Badges section (Rekaman Tersedia, Segera Hadir, Telah Lewat)

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-16 15:47:43 +07:00
dwindown
ca163e13cf Fix webinar recording detection to support M3U8 and MP4 from Adilo
The issue was that MemberAccess.tsx was only checking recording_url (YouTube)
and not fetching or checking m3u8_url, mp4_url, or video_host fields.

Changes:
- Added m3u8_url, mp4_url, video_host, and event_start to product queries
- Updated UserAccess interface to include these fields
- Changed webinar recording check to support all recording types
- Now properly detects Adilo recordings as available

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-16 14:52:14 +07:00
dwindown
713d881445 Move calendar cleanup button to status filter pills for cleaner UI
- Removed separate section for cleanup button
- Added CleanUp button inline with status filter pills
- Shorter text (CleanUp instead of full description)
- Matches the size and style of other filter buttons
- Saves vertical space on the page

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-11 23:40:40 +07:00
dwindown
9c2f367447 Add calendar cleanup button to admin consulting page
Adds a manual button for admins to trigger Google Calendar event cleanup for cancelled consulting sessions. This works around Docker networking limitations.

Features:
- Button to trigger cleanup
- Confirmation dialog
- Loading state
- Success/error toast notifications

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-11 23:32:06 +07:00
dwindown
d0d824a661 Simplify calendar cleanup: handle in SQL function, remove HTTP dependency
Due to Docker networking limitations between supabase-db and supabase-edge-functions
containers, automatic HTTP triggering of the edge function is not possible.

Changes:
- Updated cancel_expired_consulting_orders_sql() to also clear calendar_event_id
- This prevents stale references in the database
- Removed Task 2 dependency documentation (not workable without HTTP access)
- Edge function trigger-calendar-cleanup still available for manual triggering

To manually clean up Google Calendar events:
curl -X POST https://your-project.supabase.co/functions/v1/trigger-calendar-cleanup

Coolify Tasks:
- Task 1: Keep (works fine with psql)
- Task 2: DELETE (HTTP between containers doesn't work)
- Task 3: DELETE (deprecated duplicate)

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-11 23:27:18 +07:00
dwindown
e268ef7756 Update calendar cleanup task documentation
Clarify that Task 2 must run on supabase-db service which has curl/wget.
The supabase-edge-functions service doesn't have curl, wget, or deno CLI available.

Command for Task 2 (run on supabase-db):
curl -X POST http://supabase-edge-functions:8000/functions/v1/trigger-calendar-cleanup

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-11 23:23:33 +07:00
dwindown
bfc1f505bc Improve trigger-calendar-cleanup edge function with proper TypeScript types and CORS
- Add proper CORS headers
- Use standard import instead of dynamic import
- Match the style of other edge functions in the project
- Function can be called once curl/deno/wget is available in scheduled task container

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-11 23:06:38 +07:00
dwindown
1ef85a22d5 Remove unnecessary calendar cleanup scripts
Now using the trigger-calendar-cleanup edge function instead,
which is cleaner and avoids the 255 character command limit in Coolify.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-11 22:59:17 +07:00
dwindown
7165fcee9b Add trigger-calendar-cleanup edge function for Coolify scheduled tasks
Created a lightweight wrapper edge function that calls cancel-expired-consulting-orders.
This solves the Coolify scheduled_tasks command column 255 character limit.

Coolify command (184 chars):
deno run --allow-net --allow-env -e "fetch('http://supabase-edge-functions:8000/functions/v1/trigger-calendar-cleanup',{method:'POST'}).then(r=>r.json()).then(j=>console.log(JSON.stringify(j)))"

This replaces the need for long curl commands or external scripts.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-11 22:59:02 +07:00
dwindown
a1ba5f342b Add Deno script to trigger calendar cleanup without curl
Created a Deno script that uses the built-in fetch() API to trigger the
cancel-expired-consulting-orders edge function, replacing the need for curl
in Coolify scheduled tasks.

This script:
- Uses Deno.env.get() for environment variables (like Supabase functions)
- Uses Deno's native fetch() API (no external dependencies)
- Runs with --allow-net and --allow-env permissions
- Can be used in Coolify scheduled tasks: deno run scripts/trigger-calendar-cleanup.js

Usage in Coolify scheduled task command:
deno run --allow-net --allow-env /app/scripts/trigger-calendar-cleanup.js

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-11 22:29:17 +07:00
dwindown
a801e2d344 Fix chapters persisting when switching between lessons in editor
The ChaptersEditor component had its own internal state (chaptersList) that was
only initialized once from the chapters prop. When switching between lessons,
the prop would change but the internal state wouldn't update, causing the
previous lesson's chapters to persist.

Added a useEffect to sync the internal state whenever the chapters prop changes.
Now when you switch from lesson 3 (with chapters) to lesson 2 (no chapters),
the editor properly resets to show a single empty chapter.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-04 20:29:43 +07:00
dwindown
269e384665 Fix chapter pollution when switching between lessons in curriculum editor
The issue was that handleEditLesson was setting lessonForm.chapters to a direct
reference of lesson.chapters instead of creating a copy. This caused mutations
to the form to also modify the lesson objects in the lessons array state.

When editing lesson 3 with chapters, then switching to edit lesson 2 (which had
no chapters), the form would still show lesson 3's chapters because it was
referencing the same array object.

Fix: Use spread operator [...lesson.chapters] to create a shallow copy of the
chapters array, preventing shared references between form state and lessons state.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-04 20:10:11 +07:00
dwindown
d6126d1943 Fix admin redirect by using isAdmin from auth context instead of user_metadata.role
The root cause was that ProtectedRoute and Auth.tsx were checking user.user_metadata?.role,
but the admin role is stored in the user_roles table, not in user metadata.

Changes:
- ProtectedRoute: Use isAdmin flag from useAuth context instead of user.user_metadata?.role
- Auth.tsx: Use isAdmin flag for role-based redirect logic
- Remove redundant auth checks from individual admin/member pages (ProtectedRoute handles it)
- Add isAdmin to useEffect dependencies to ensure redirect happens after admin check completes

This fixes the issue where admins were being redirected to /dashboard instead of /admin
after login, because the role check was happening before the async admin role lookup completed.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-04 19:04:10 +07:00
dwindown
a423a6d31d Simplify ProtectedRoute to fix blank page issue
Removed hasChecked ref logic that was causing rendering issues. The key insight is that the effect should run when user changes, but only redirect if the condition doesn't match.

## Changes:
- Remove hasCheckedRef complexity
- Simplify effect to only redirect when conditions don't match
- Split admin check into separate condition that only runs when user exists
- This prevents unnecessary redirects while allowing normal rendering

## Root Cause:
The hasChecked logic was preventing the component from rendering properly on navigation.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-04 18:21:06 +07:00
dwindown
87539eb51f Fix ProtectedRoute blank page issue
Changed from useState to useRef for hasChecked flag to prevent loading state on route navigation. Each ProtectedRoute instance now properly renders immediately after auth check completes.

## Changes:
- Use useRef instead of useState for hasChecked flag
- Remove !hasChecked from loading condition
- This allows immediate rendering after first auth check
- Prevents blank pages when navigating between admin routes

## Technical Details:
- useState caused each new route to start with hasChecked=false
- useRef persists the check but each route instance is independent
- Combined with removing !hasChecked from loading check, pages render immediately

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-04 18:13:26 +07:00
dwindown
e4a09a676e Fix navigation issues with ProtectedRoute
Fixed infinite redirect loop and navigation blocking in ProtectedRoute component. The issue was that the useEffect was running on every render when navigating between admin routes.

## Changes:
- Added hasChecked state to ensure effect only runs once per mount
- Prevents multiple redirects and navigation blocking
- Allows smooth navigation between admin and member pages after login

## Technical Details:
- Before: useEffect ran on every render with requireAdmin in dependencies
- After: useEffect runs once when auth loading completes
- This prevents React Router navigation conflicts

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-04 18:05:38 +07:00
dwindown
e79e982401 Collapse lesson timelines by default for cleaner UX
Changed timeline accordion behavior to keep all lesson chapters collapsed on page load. This prevents overwhelming users with too much content when viewing bootcamp curriculum previews.

## Before:
- All lesson timelines expanded by default
- 3 lessons × 8 chapters = 24+ items visible (~1500px+ scroll)
- Overwhelming visual clutter on product detail pages

## After:
- All timelines collapsed by default
- Shows "N timeline items" hint for each lesson
- Users can expand individual lessons they're interested in
- Cleaner, more professional appearance
- Better for conversion - product details remain prominent

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-04 16:05:20 +07:00
dwindown
aeeb02d36b Add authentication protection to admin and member routes
CRITICAL SECURITY FIX: All admin and member routes now require authentication.

## Changes:
- Created ProtectedRoute component to enforce authentication
- Protected all member routes (/dashboard, /access, /orders, /profile)
- Protected all admin routes (/admin/*) with admin role check
- Added redirect-after-login functionality using sessionStorage
- Non-authenticated users accessing protected pages are redirected to /auth
- Non-admin users accessing admin pages are redirected to /dashboard

## Security Impact:
- Prevents unauthorized access to admin panel and member areas
- Users must login to access any protected functionality
- Admin routes additionally verify user role is 'admin'
- After login, users are redirected back to their intended page

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-04 15:24:34 +07:00
dwindown
47a645520c Fix curriculum editor chapters persisting across lessons
Fixed bug where editing a lesson without chapters would show chapters from the previously edited lesson. The handleNewLesson function now explicitly initializes chapters to an empty array to prevent state from carrying over between lesson edits.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-04 15:05:01 +07:00
dwindown
8d40a8cb29 Add collapsible timeline accordion to bootcamp curriculum preview
- Added state for expanded lesson chapters (expandedLessonChapters)
- Created toggleLessonChapters helper function
- Fixed formatChapterTime to support hours (1:11:11 instead of 111:11)
- Wrapped lesson timeline in Collapsible component with Clock icon
- Timeline header shows "N timeline items" count
- All timelines expanded by default on page load
- Users can collapse/expand to focus on one lesson at a time
- ChevronDown/ChevronRight icons indicate expanded/collapsed state

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-04 13:25:45 +07:00
dwindown
d126f2d9c6 Add public bootcamp curriculum access migration
- Created migration to enable public read access to bootcamp curriculum
- RLS policies on bootcamp_modules and bootcamp_lessons for public/authenticated roles
- Allows kurikulum card to display on product detail pages for unauthenticated users
- Users can now see curriculum preview before purchasing

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-04 13:22:01 +07:00
dwindown
7cc8d47ecf Add public read access to bootcamp curriculum tables
- Enable RLS on bootcamp_modules and bootcamp_lessons tables
- Add public SELECT policies so unauthenticated users can view curriculum
- This allows kurikulum card to be displayed on bootcamp product detail pages
- Both public and authenticated roles can read the curriculum data

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-04 12:34:23 +07:00
dwindown
71d6da4530 Fix "Lanjutkan" resume button to jump to saved position
- Change jumpToTime to check video.seekable instead of adiloPlayer.isReady
- Wait for canplay event if video is not seekable yet
- This fixes issue where resume button started from 00:00 instead of saved position
- Added better console logging for debugging

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-04 11:56:12 +07:00
dwindown
8fc31b402d Fix timeline chapter click by passing missing props to VideoPlayer
- Pass playerRef, currentTime, accentColor, and setCurrentTime to VideoPlayer
- This fixes "Cannot read properties of undefined (reading 'current')" error
- Timeline chapter items can now successfully jump to specific timestamps

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-04 11:46:07 +07:00
dwindown
15760d6430 Fix React error #310 by ensuring hooks are called before conditional returns
- Moved useMemo calls before early returns in VideoPlayer component
- This ensures hooks are always called in the same order on every render
- Fixed violation of Rules of Hooks that caused error in production

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-04 11:39:39 +07:00
dwindown
ab7033b82e Fix bootcamp video reloading issue and duplicate component error
- Moved VideoPlayer component outside main Bootcamp component to prevent re-creation on every render
- Memoized video source object and URL values to ensure stability
- Removed duplicate Bootcamp component declaration that caused build failure
- Video player now persists across Bootcamp re-renders, fixing continuous reload from 00:00
- Timeline clicking now works correctly without triggering video reload

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-04 11:34:49 +07:00
dwindown
b7bde1df04 Fix video player reloading by moving VideoPlayer component outside main component
- Move VideoPlayer component outside Bootcamp component to prevent re-creation on every render
- This prevents useAdiloPlayer and all hooks from re-initializing unnecessarily
- Video now plays and jumps correctly without reloading
- Component structure now matches WebinarRecording page pattern

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-04 11:25:01 +07:00
dwindown
2b98a5460d Fix video reloading issue by memoizing video source and resetting resume prompt
- Memoize video source object in Bootcamp to prevent unnecessary re-renders
- Reset resume prompt flag when videoId changes to allow prompt for new lessons
- Remove key prop from VideoPlayerWithChapters to prevent unmounting
- Video now plays correctly without reloading on every interaction

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-04 11:17:38 +07:00
dwindown
44484afb84 Fix timeline borders, revert sidebar accordion, and fix video reloading
- Remove border-bottom from TimelineChapters component for better readability
- Revert collapsible timeline changes from bootcamp sidebar
- Fix video reloading issue by adding key prop to force remount on lesson change
- Prevent resume prompt from showing multiple times during video playback
- Resume prompt now only shows once when component mounts

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-04 11:09:59 +07:00
dwindown
963160d165 Improve Bootcamp timeline with collapsible modules and HTML support
- Add collapsible structure: Module → Lesson → Timeline (as sublesson)
- Support HTML in timeline titles with DOMPurify sanitization
- Add inline code styling for timeline content
- Fix vertical alignment (items-start instead of items-center)
- Add soft borders between timeline items
- Change layout from 2-column grid to single column (video full width, timeline below)
- Align Bootcamp page layout with WebinarRecording page

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-04 10:47:59 +07:00
dwindown
ce10be63f3 Fix content field not loading in product edit form
- Add 'content' field to fetchProducts SELECT query
- Fixes Rich Text Editor showing empty when editing products

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-03 22:25:24 +07:00
dwindown
8217261706 replace favicon.ico 2026-01-03 21:46:25 +07:00
dwindown
053465afa3 Fix email system and implement OTP confirmation flow
Email System Fixes:
- Fix email sending after payment: handle-order-paid now calls send-notification
  instead of send-email-v2 directly, properly processing template variables
- Fix order_created email timing: sent immediately after order creation,
  before payment QR code generation
- Update email templates to use short order ID (8 chars) instead of full UUID
- Add working "Akses Sekarang" buttons to payment_success and access_granted emails
- Add platform_url column to platform_settings for email links

OTP Verification Flow:
- Create dedicated /confirm-otp page for users who close registration modal
- Add link in checkout modal and email to dedicated OTP page
- Update OTP email template with better copywriting and dedicated page link
- Fix send-auth-otp to fetch platform settings for dynamic brand_name and platform_url
- Auto-login users after OTP verification in checkout flow

Admin Features:
- Add delete user functionality with cascade deletion of all related data
- Update IntegrasiTab to read/write email settings from platform_settings only
- Add test email template for email configuration testing

Cleanup:
- Remove obsolete send-consultation-reminder and send-test-email functions
- Update send-email-v2 to read email config from platform_settings
- Remove footer links (Ubah Preferensi/Unsubscribe) from email templates

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-03 18:02:25 +07:00
dwindown
4f9a6f4ae3 Fix variable replacement format in send-notification
The replaceVariables function was only supporting {{key}} format but
the email templates use {key} format (single braces). Updated to support
both formats for compatibility.

Changes:
- Added support for {key} format in addition to {{key}}
- Ensures all template variables are properly replaced

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-03 09:10:19 +07:00
dwindown
9f8ee0d7d2 Add Mailketing API support to send-notification function
The send-notification function was using SMTP by default and failing with
ConnectionRefused errors. Added Mailketing API integration to match the
email provider used by other functions (send-auth-otp, send-email-v2).

Changes:
- Added sendViaMailketing() function with form-encoded body
- Added "mailketing" case to provider switch (now the default)
- Changed default provider from "smtp" to "mailketing"
- Reads API token from mailketing_api_token or api_token settings

This ensures order_created emails are sent successfully via Mailketing
just like other email notifications in the system.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-03 09:06:27 +07:00
dwindown
1fbaf4d360 Fix column names in send-notification and migration
Updated to use the correct database column names:
- email_subject (not subject)
- email_body_html (not body_html)

Changes:
- send-notification/index.ts: Added fallback to check both email_subject/subject
  and email_body_html/body_html for compatibility
- Migration: Updated to use correct column names (email_subject, email_body_html)

This matches the actual database schema shown in the notification_templates table.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-03 08:59:49 +07:00
dwindown
485263903f Add order_created email template with QR code section
This migration updates the order_created email template to include:
- QR code image display for payment
- Order summary section with all details
- Payment link button
- QR expiry time display
- Professional layout and styling

The template uses these shortcodes:
- {qr_code_image} - Base64 QR code image generated by send-notification
- {qr_expiry_time} - QR code expiration timestamp
- {nama}, {email}, {order_id}, {tanggal_pesanan}
- {total}, {metode_pembayaran}, {produk}
- {payment_link}, {thank_you_page}, {platform_name}

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-03 08:53:31 +07:00
dwindown
00de020b6c Fix template lookup column name in send-notification
The function was using .eq("template_key") but the actual column name
in notification_templates table is "key". This caused "Template not found"
errors even when the template existed and was active.

Changes:
- send-notification/index.ts: Changed .eq("template_key") to .eq("key")
- Matches the pattern used in send-auth-otp and other edge functions

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-03 08:52:32 +07:00
dwindown
5f753464fd Fix order_created email by waiting for send-notification before navigation
The early termination issue was caused by the page navigating away before
the send-notification function call could complete. Changed from fire-and-forget
to awaiting the email send result before redirecting.

Changes:
- Checkout.tsx: Changed send-notification call to use await instead of .then()/.catch()
- Now waits for email send to complete before navigating to order detail page
- Email failures are caught in try/catch but navigation still proceeds

Technical details:
- Browser was terminating pending requests when navigate() was called immediately
- Early termination: isolate warning indicated function was being killed mid-execution
- Awaiting the function call ensures it completes before page navigation

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-03 07:40:59 +07:00
dwindown
1749056542 Add order_created email with QR code generation
Enhanced email notification system to send order confirmation emails immediately after order creation with embedded QR code for payment.

Changes:
- Checkout.tsx: Added send-notification call after payment creation with comprehensive logging
- send-notification: Added QRCode library integration for generating base64 QR images for order_created emails
- NotifikasiTab.tsx: Added QR code section to default order_created template and updated shortcodes list

Technical details:
- QR code generated as base64 data URL for email client compatibility
- Fire-and-forget pattern ensures checkout flow isn't blocked
- Added detailed console logging for debugging email send issues
- New shortcodes: {qr_code_image}, {qr_expiry_time}

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-03 06:58:55 +07:00
dwindown
2ce5c2efe8 Fix missing signIn and signUp destructuring in Checkout
The auth functions were being called but not destructured from useAuth hook,
causing ReferenceError at runtime.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-02 20:57:34 +07:00
dwindown
72799b981d Add timeline clickability control and checkout auth modal
- Add clickable prop to TimelineChapters component for marketing vs purchased users
- Make timeline non-clickable in product preview pages with lock icons
- Keep timeline clickable in actual content pages (WebinarRecording, Bootcamp)
- Add inline auth modal in checkout with login/register tabs
- Replace "Login untuk Checkout" button with seamless auth flow
- Add form validation and error handling for auth forms

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-02 20:38:43 +07:00
dwindown
eee6339074 Fix email unconfirmed login flow with OTP resend and update email API field names 2026-01-02 19:33:51 +07:00
dwindown
8f46c5cfd9 Add EmailComponents and ShortcodeProcessor to shared email template renderer
- Add EmailComponents utility functions (button, alert, otpBox, etc.)
- Add ShortcodeProcessor class with DEFAULT_DATA
- Now matches src/lib/email-templates/master-template.ts exactly
- Edge functions can now use helpful components like EmailComponents.otpBox()
2026-01-02 17:20:48 +07:00
dwindown
74bc709684 Add current status document with remaining work 2026-01-02 17:17:22 +07:00