Commit Graph

167 Commits

Author SHA1 Message Date
dwindown
9fdcf07439 Add RLS policy fixes for platform_settings and storage
- PLATFORM_SETTINGS_RLS_FIX.sql: Allow public read access to branding settings
- STORAGE_RLS_FIX.sql: Fix upload permissions for logo/favicon

These fixes:
1. Allow non-admin users to see branding (logo, favicon, colors)
2. Fix empty JSON response on platform_settings fetch
3. Fix storage upload 403 errors

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-24 14:40:14 +07:00
dwindown
7a8f9cb9a9 Fix logo/favicon upload, badge colors, and page title issues
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>
2025-12-24 12:31:48 +07:00
dwindown
3af2787d03 Add deployment guide for post-implementation refinements 2025-12-24 11:42:51 +07:00
dwindown
fb24e77e42 Implement post-implementation refinements
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>
2025-12-24 11:42:20 +07:00
dwindown
4b8765885b Fix broken Checkout.tsx - remove leftover waiting step code 2025-12-24 00:32:14 +07:00
dwindown
35a003e35c Add QR code display and polling to OrderDetail page
- 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>
2025-12-24 00:25:27 +07:00
dwindown
eba37df4d7 Remove PayPal, simplify to QRIS-only with in-app QR display
- 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>
2025-12-24 00:12:04 +07:00
dwindown
1a36f831cc Refactor: Rename create-pakasir-payment to create-payment
- 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>
2025-12-23 21:41:47 +07:00
dwindown
a9f7c9b07a Create Pakasir payment edge function to fix CORS issue
- 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>
2025-12-23 21:20:40 +07:00
dwindown
94403bd634 Add order deletion functionality
- 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>
2025-12-23 18:06:40 +07:00
dwindown
e6b1e02e5f Fix consulting payment: call Pakasir API directly from frontend
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>
2025-12-23 17:21:01 +07:00
dwindown
ecab3eb22a Fix consulting booking: bypass cart, go directly to payment
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>
2025-12-23 17:16:20 +07:00
dwindown
01579ac299 Refactor payment flow to use database triggers (Clean Architecture)
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>
2025-12-23 16:59:13 +07:00
dwindown
9d7d76b04d Add consulting slots display with Join Meet button
- 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>
2025-12-23 16:45:48 +07:00
dwindown
ce531c8d46 Add Google Meet event creation to payment webhook
When order is paid, automatically create Google Meet events for all consulting slots.
The meet_link is saved to consulting_slots table and included in notifications.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-23 16:41:22 +07:00
dwindown
7bf13b88d2 Add detailed debug info to edge function response
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-23 16:27:33 +07:00
dwindown
3f8c2b7c01 Fix body consumption: use req.text() instead of req.json()
Using req.text() first then parsing JSON gives us more control and avoids
stream consumption issues with Deno/Supabase edge functions.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-23 16:14:56 +07:00
dwindown
8e476a7a82 Fix body consumed error: disable JWT verification for create-google-meet-event
The JWT verification was consuming the request body before our handler could read it.
Since this function is called from authenticated sessions, we verify differently.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-23 16:02:23 +07:00
dwindown
0e776046b4 Fix React Strict Mode double-call issue in Google Calendar integration
- Add state-based lock (isTestRunning) to prevent duplicate API calls
- Update error handling in edge function for body consumption
- Add OAuth2 token generation helper tool (get-google-token-local.html)

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-23 15:11:56 +07:00
dwindown
8f167c85a8 Handle 'Body already consumed' from React Strict Mode duplicate calls
- Catch TypeError when req.json() is called on consumed stream
- Return success response for duplicate calls (first call handles the actual work)
- This handles React Strict Mode firing onClick twice in parallel
- Only the first call that successfully reads body will process the request
2025-12-23 15:07:11 +07:00
dwindown
689db9eed1 Add button debouncing to prevent double API calls
- Disable button while request is in progress
- Re-enable button after request completes (success or error)
- Prevents React Strict Mode from firing duplicate simultaneous requests
- Fixes 'Body already consumed' error from parallel edge function calls
2025-12-23 15:01:02 +07:00
dwindown
d358d95486 Clean up unused pendingRequests variable 2025-12-23 14:55:52 +07:00
dwindown
cc66e96f61 Fix 'Body already consumed' error by using req.clone()
- Use req.clone().json() to handle multiple reads from same request
- This happens when React Strict Mode fires requests twice
- Cloning the request allows reading body multiple times safely
2025-12-23 14:52:40 +07:00
dwindown
e2d22088c1 Implement token caching to avoid unnecessary refresh token calls
- Add expires_at timestamp to OAuth config
- Cache access_token in database to reuse across requests
- Only refresh token when it expires (after 1 hour)
- Use 60-second buffer to avoid using almost-expired tokens
- Auto-update cached token after refresh
- This fixes the invalid_grant error from excessive refresh calls
2025-12-23 14:48:55 +07:00
dwindown
7d22a5328f Switch from Service Account to OAuth2 for Google Calendar (Personal Gmail)
- Replace JWT service account authentication with OAuth2 refresh token flow
- Service accounts cannot create Google Meet links for personal Gmail accounts
- Update edge function to use OAuth2 token exchange
- Change database column from google_service_account_json to google_oauth_config
- Add helper tool (get-google-refresh-token.html) to generate OAuth credentials
- Update IntegrasiTab UI to show OAuth config instead of service account
- Add SQL migration file for new google_oauth_config column

OAuth2 Config format:
{
  "client_id": "...",
  "client_secret": "...",
  "refresh_token": "..."
}

This approach works with personal @gmail.com accounts without requiring
Google Workspace or Domain-Wide Delegation.
2025-12-23 14:06:42 +07:00
dwindown
286ab630ea Revert to simple hangoutsMeet type in conferenceSolutionKey
- Try just 'hangoutsMeet' as type without 'name' field
- This is the most basic format according to some docs
- Combined with full event logging to debug
2025-12-23 11:49:03 +07:00
dwindown
29a58daed4 Add conferenceData entryPoints parsing and full event logging
- Check conferenceData.entryPoints for video meet link
- Keep hangoutLink as fallback for backwards compatibility
- Log full event response to debug missing meet link
- This will show us what Google actually returns
2025-12-23 11:46:54 +07:00
dwindown
e62caa3ddb Simplify conferenceData and add debug logging
- Remove conferenceSolutionKey entirely - let Google auto-select Meet
- Add console.log to see event data being sent
- Add response status logging for debugging
2025-12-23 11:45:34 +07:00
dwindown
9f2d36b5f5 Fix conferenceSolutionKey structure for Google Meet
- Change type from 'hangoutsMeet' to 'event'
- Add name: 'hangoutsMeet' property
- This matches Google Calendar API requirements for creating Meet conferences
2025-12-23 11:43:56 +07:00
dwindown
23f8f70c83 Remove attendees from calendar event to work without Domain-Wide Delegation
- Service accounts require Domain-Wide Delegation to invite attendees
- Removed attendees array, sendUpdates, and guest permissions
- Client email still included in event description
- Google Meet link will still be generated successfully
- Can re-enable attendees later if Domain-Wide Delegation is configured
2025-12-23 11:37:53 +07:00
dwindown
bc8bc1159d Fix JWT generation using native Deno Web Crypto API
- Remove external jose library dependency that was causing import errors
- Use native crypto.subtle API available in Deno
- Manual base64url encoding for JWT header, payload, and signature
- Use RSASSA-PKCS1-v1_5 with SHA-256 for RS256 algorithm
- Remove cat heredoc wrapper from file
2025-12-23 11:25:20 +07:00
dwindown
43305a2f16 Rewrite JWT generation using manual construction and Web Crypto API
- Build JWT header and payload manually for exact format control
- Use lower-level importKey/sign from Web Crypto API
- Use RSASSA-PKCS1-v1_5 algorithm directly (RSA+SHA256 = RS256)
- Manual base64url encoding for URL-safe tokens
- Add debug logging to trace JWT generation
- Avoids SignJWT abstraction that was causing algorithm errors
2025-12-23 11:17:55 +07:00
dwindown
0ad50f4b6b Remove keyId from importPKCS8 options
- Remove keyId parameter from importPKCS8 call
- Keep kid in protected header for JWT
- Fix 'Invalid or unsupported alg value' error

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-23 11:11:57 +07:00
dwindown
1f998c2549 Fix private key import for JWT signing
- Use importPKCS8 to convert private key string to CryptoKey
- Pass CryptoKey to SignJWT.sign() instead of string
- Fix 'Key for RS256 algorithm must be CryptoKey' error

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-23 11:08:36 +07:00
dwindown
fa1064daac Fix JWT signing for Google Calendar authentication
- Change from jwt.sign() to SignJWT class
- Use proper jose library SignJWT API for Deno
- Fix 'Cannot read properties of undefined' error

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-23 11:04:40 +07:00
dwindown
ee019ea767 Fix column name mismatch for service account JSON
- Change integration_google_service_account_json to google_service_account_json
- Matches actual database column name
- Remove schema cache workaround since column name now matches
- Update all frontend references to use correct column name

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-23 10:57:09 +07:00
dwindown
7c2a084b3e Add schema cache fallback for service account JSON
- Add fallback RPC method when PostgREST schema cache fails
- Save service account JSON separately via raw SQL
- Show warning message if manual save needed

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-23 10:52:00 +07:00
dwindown
00e6ef17d6 Add create-google-meet-event to config.toml and deploy
- Add function configuration to supabase/config.toml
- Successfully deploy via deploy-edge-functions.sh

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-23 10:36:59 +07:00
dwindown
0a3aca7cbc Add deployment helpers and environment config
- Add manual deployment instructions for self-hosted Supabase
- Add schema refresh SQL scripts
- Add deployment helper scripts
- Add Supabase environment configuration

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-23 10:26:06 +07:00
dwindown
6e411b160a Fix edge function and add to deployment script
- Update create-google-meet-event with improved JWT handling
- Fix jose library import and token signing
- Add better error logging
- Include create-google-meet-event in deployment script

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-23 09:58:12 +07:00
dwindown
631dc9a083 Add Google Calendar integration via Supabase Edge Functions
- Create new create-google-meet-event edge function
- Use service account authentication (no OAuth needed)
- Add google_service_account_json field to platform_settings
- Add admin UI for service account JSON configuration
- Include test connection button in Integrasi tab
- Add comprehensive setup documentation
- Keep n8n workflows as alternative option

Features:
- Direct Google Calendar API integration
- JWT authentication with service account
- Auto-create Google Meet links
- No external dependencies needed
- Simple configuration via admin panel

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-23 01:32:23 +07:00
dwindown
dfda71053c Add n8n test mode toggle and edge function improvements
- Add test/production toggle for n8n webhook URLs in IntegrasiTab
- Update create-meet-link function to use database test_mode setting
- Add send-email-v2 edge function for Mailketing API integration
- Update daily-reminders and send-consultation-reminder to use send-email-v2
- Remove deprecated branding field from BrandingTab
- Update domain references from hub.dwindi.com to with.dwindi.com
- Add environment variables for Coolify deployment
- Add comprehensive edge function test script
- Update payment flow redirect to order detail page

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-23 00:24:40 +07:00
dwindown
f1cc2ba3f7 Fix checkout flow to redirect to specific order detail page
🔄 Better UX Flow:
- After successful payment: redirect to /orders/{order_id} instead of /access
- This shows the specific order details where users can see their purchase
- Users can then proceed to payment if needed or access their content

📧 Updated Shortcodes:
- {thank_you_page} now uses dynamic URL pattern: /orders/{order_id}
- {order_id} will be replaced with actual order ID by ShortcodeProcessor
- Added {thank_you_page} to order_created and payment_reminder templates

🎯 User Journey:
1. User receives email with payment link
2. After successful payment → redirected to specific order detail page
3. If not logged in → redirects to login, then back to order detail
4. Order detail page shows payment status and access information

Much better user experience than generic dashboard redirect!

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-22 22:40:08 +07:00
dwindown
7fbc7c1302 Fix shortcode URLs to use actual application routes
🔧 Fixed URLs:
- {thank_you_page} now points to actual /access route (Member Access page)
- {payment_link} now points to /checkout route instead of dummy URL

 Based on real app flow analysis:
- After successful payment, users are redirected to /access (MemberAccess.tsx)
- Payment initiation happens through /checkout page
- These routes are verified in App.tsx routing configuration

This ensures email templates use real, functional URLs that match the actual application flow.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-22 22:32:53 +07:00
dwindown
7244433e12 Improve shortcode UI and add payment link shortcodes
 UI Improvements:
- Completely redesigned shortcode display with clean categorization
- Added collapsible section for all available shortcodes
- Used emoji icons for better visual organization
- Improved color coding and typography
- Added "Used in this template" section with visual distinction

 New Shortcodes:
- Added {payment_link} for direct payment links in emails
- Added {thank_you_page} for public thank you page access
- Updated relevant templates to include new payment shortcodes

🎯 Key Features:
- Shortcodes organized by category (User, Order, Product, Access, etc.)
- Visual hierarchy with proper spacing and borders
- Hover effects and smooth transitions
- Better readability with proper contrast

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-22 22:17:51 +07:00
dwindown
3f8cd7937a Fix JavaScript error in EmailTemplatePreview
- Fix "nama is not defined" error by properly escaping shortcode text
- Wrap template literals with backticks in JSX to prevent variable interpretation
- This prevents shortcode braces from being treated as JavaScript variables

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-22 22:07:54 +07:00
dwindown
204218c4e7 Fix database constraint error in template seeding
- Replace insert() with upsert() to handle existing templates
- Add onConflict: 'key' to update duplicates instead of failing
- This resolves "duplicate key value violates unique constraint" error
- Templates will now be properly updated/seeded on first visit

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-22 21:57:00 +07:00
dwindown
78e7b946ac Fix shortcode organization and add template debugging
🎯 **Shortcode Organization:**
- Replace overwhelming all-shortcodes display with per-template relevant shortcodes
- Each notification type now shows only shortcodes that make sense for that context
- Examples:
  - Payment templates: order info, amounts, payment details
  - Access templates: login credentials, access links
  - Consulting templates: meeting details, consultation topics
  - Event templates: event info, schedules, locations

🐛 **Template Content Debugging:**
- Auto-detect templates with empty content and force reseed
- Add "Reset Template Default" button for manual debugging
- Enhanced console logging for template loading issues
- Force reseed functionality to delete and recreate templates

 **UI Improvements:**
- Remove unused Textarea import
- Cleaner shortcode display within each template card
- Better user experience with focused, relevant information

🔧 **Debug Features:**
- Check for empty email_subject or email_body_html fields
- Automatic content repair when empty templates are detected
- Manual reset option for troubleshooting
- Detailed console logging for development

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-22 21:38:47 +07:00
dwindown
edca7205ef Improve shortcode UI display in NotifikasiTab and EmailTemplatePreview
 Enhanced shortcode organization and display:

**NotifikasiTab improvements:**
- Organized shortcodes into 9 categories with color-coded badges
- User Info (blue), Order Info (green), Product Info (yellow), Access Info (purple)
- Consulting (orange), Event Info (pink), Bootcamp Info (indigo), Company Info (gray), Payment Info (red)
- Added scrollable container with max-height for better UX
- Better visual hierarchy with category headers

**EmailTemplatePreview improvements:**
- Scrollable shortcode list with grid layout (2-3 columns)
- Show only shortcodes used in current template
- Add "All Available Shortcodes" summary section
- Improved visual organization with consistent styling

🎨 UI/UX Benefits:
- Easier to find relevant shortcodes by category
- Visual distinction between different data types
- Better space utilization with scrollable areas
- More informative and scannable layout

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-22 21:18:30 +07:00
dwindown
0668ed22a7 Fix email template preview UX and add comprehensive shortcodes
1. Remove duplicate close button in preview modal
- Removed extra X button in DialogHeader since Dialog already has close functionality

2. Fix test email to include master layout
- Process shortcodes and render with master template before sending
- Import EmailTemplateRenderer and ShortcodeProcessor in sendTestEmail
- Send complete HTML email with header, footer, and styling applied

3. Add comprehensive table shortcodes for all notification types
- User info: {nama}, {email}
- Order info: {order_id}, {tanggal_pesanan}, {total}, {metode_pembayaran}, {status_pesanan}, {invoice_url}
- Product info: {produk}, {kategori_produk}, {harga_produk}, {deskripsi_produk}
- Access info: {link_akses}, {username_akses}, {password_akses}, {kadaluarsa_akses}
- Consulting: {tanggal_konsultasi}, {jam_konsultasi}, {durasi_konsultasi}, {jenis_konsultasi}, {topik_konsultasi}
- Event: {judul_event}, {tanggal_event}, {jam_event}, {link_event}, {lokasi_event}, {kapasitas_event}
- Bootcamp: {judul_bootcamp}, {progres_bootcamp}, {modul_selesai}, {modul_selanjutnya}, {link_progress}
- Company: {nama_perusahaan}, {website_perusahaan}, {email_support}, {telepon_support}
- Payment: {bank_tujuan}, {nomor_rekening}, {atas_nama}, {jumlah_pembayaran}, {batas_pembayaran}

4. Improve shortcode UI display
- Scrollable shortcode list with better organization
- Show available shortcodes summary
- Categorize shortcodes by type (User, Orders, Products, etc.)

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-22 21:13:09 +07:00