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>
This commit is contained in:
dwindown
2025-12-22 21:38:47 +07:00
parent edca7205ef
commit 78e7b946ac

View File

@@ -5,7 +5,6 @@ import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Switch } from '@/components/ui/switch';
import { Textarea } from '@/components/ui/textarea';
import { Alert, AlertDescription } from '@/components/ui/alert';
import { RichTextEditor } from '@/components/RichTextEditor';
import { toast } from '@/hooks/use-toast';
@@ -24,25 +23,14 @@ interface NotificationTemplate {
last_payload_example: Record<string, unknown> | null;
}
const SHORTCODES_HELP = {
// User information
common: ['{nama}', '{email}'],
// Order information
orders: ['{order_id}', '{tanggal_pesanan}', '{total}', '{metode_pembayaran}', '{status_pesanan}', '{invoice_url}'],
// Product information
products: ['{produk}', '{kategori_produk}', '{harga_produk}', '{deskripsi_produk}'],
// Access information
access: ['{link_akses}', '{username_akses}', '{password_akses}', '{kadaluarsa_akses}'],
// Consulting information
consulting: ['{tanggal_konsultasi}', '{jam_konsultasi}', '{durasi_konsultasi}', '{link_meet}', '{jenis_konsultasi}', '{topik_konsultasi}'],
// Event information
event: ['{judul_event}', '{tanggal_event}', '{jam_event}', '{link_event}', '{lokasi_event}', '{kapasitas_event}'],
// Bootcamp/Course information
bootcamp: ['{judul_bootcamp}', '{progres_bootcamp}', '{modul_selesai}', '{modul_selanjutnya}', '{link_progress}'],
// Company information
company: ['{nama_perusahaan}', '{website_perusahaan}', '{email_support}', '{telepon_support}'],
// Payment information
payment: ['{bank_tujuan}', '{nomor_rekening}', '{atas_nama}', '{jumlah_pembayaran}', '{batas_pembayaran}'],
const RELEVANT_SHORTCODES = {
'payment_success': ['{nama}', '{email}', '{order_id}', '{tanggal_pesanan}', '{total}', '{metode_pembayaran}', '{produk}', '{link_akses}'],
'access_granted': ['{nama}', '{email}', '{produk}', '{link_akses}', '{username_akses}', '{password_akses}', '{kadaluarsa_akses}'],
'order_created': ['{nama}', '{email}', '{order_id}', '{tanggal_pesanan}', '{total}', '{metode_pembayaran}', '{produk}'],
'payment_reminder': ['{nama}', '{email}', '{order_id}', '{tanggal_pesanan}', '{total}', '{metode_pembayaran}', '{batas_pembayaran}', '{jumlah_pembayaran}', '{bank_tujuan}', '{nomor_rekening}'],
'consulting_scheduled': ['{nama}', '{email}', '{tanggal_konsultasi}', '{jam_konsultasi}', '{durasi_konsultasi}', '{link_meet}', '{jenis_konsultasi}', '{topik_konsultasi}'],
'event_reminder': ['{nama}', '{email}', '{judul_event}', '{tanggal_event}', '{jam_event}', '{link_event}', '{lokasi_event}', '{kapasitas_event}'],
'bootcamp_progress': ['{nama}', '{email}', '{judul_bootcamp}', '{progres_bootcamp}', '{modul_selesai}', '{modul_selanjutnya}', '{link_progress}'],
};
const DEFAULT_TEMPLATES: { key: string; name: string; defaultSubject: string; defaultBody: string }[] = [
@@ -385,10 +373,19 @@ export function NotifikasiTab() {
}
console.log('Templates data:', templatesData);
console.log('Template count:', templatesData?.length);
if (templatesData && templatesData.length > 0) {
console.log('Setting templates from database:', templatesData.length);
// Check if any templates have empty content
const emptyTemplates = templatesData.filter(t => !t.email_subject || !t.email_body_html);
if (emptyTemplates.length > 0) {
console.log('Found templates with empty content:', emptyTemplates.map(t => ({ key: t.key, name: t.name })));
console.log('Reseeding templates with empty content...');
await forceSeedTemplates();
} else {
setTemplates(templatesData);
}
} else {
console.log('No templates found, seeding default templates...');
// Seed default templates if none exist
@@ -406,6 +403,22 @@ export function NotifikasiTab() {
}
};
const forceSeedTemplates = async () => {
try {
console.log('Force reseed: Deleting existing templates...');
// Delete existing templates
const { error: deleteError } = await supabase.from('notification_templates').delete().neq('id', '');
if (deleteError) {
console.error('Error deleting templates:', deleteError);
}
console.log('Force reseed: Inserting default templates...');
await seedTemplates();
} catch (error) {
console.error('Error in forceSeedTemplates:', error);
}
};
const seedTemplates = async () => {
try {
console.log('Seeding default templates...');
@@ -550,6 +563,19 @@ export function NotifikasiTab() {
Gunakan Mailketing API untuk pengiriman email yang andal.
</AlertDescription>
</Alert>
<div className="mt-3 pt-3 border-t">
<Button
variant="outline"
size="sm"
onClick={forceSeedTemplates}
className="text-xs"
>
🔄 Reset Template Default
</Button>
<span className="text-xs text-muted-foreground ml-2">
Gunakan jika template kosong atau bermasalah
</span>
</div>
</CardContent>
</Card>
@@ -564,80 +590,7 @@ export function NotifikasiTab() {
<CardContent className="space-y-4">
<div className="text-sm text-muted-foreground p-3 bg-muted rounded-md space-y-2">
<p className="font-medium">Shortcode yang tersedia:</p>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3 max-h-48 overflow-y-auto">
<div>
<span className="font-medium block mb-1">User Info:</span>
<div className="text-xs space-y-1">
{SHORTCODES_HELP.common.map(code => (
<code key={code} className="bg-blue-100 px-1 py-0.5 rounded text-xs mr-1">{code}</code>
))}
</div>
</div>
<div>
<span className="font-medium block mb-1">Order Info:</span>
<div className="text-xs space-y-1">
{SHORTCODES_HELP.orders.map(code => (
<code key={code} className="bg-green-100 px-1 py-0.5 rounded text-xs mr-1">{code}</code>
))}
</div>
</div>
<div>
<span className="font-medium block mb-1">Product Info:</span>
<div className="text-xs space-y-1">
{SHORTCODES_HELP.products.map(code => (
<code key={code} className="bg-yellow-100 px-1 py-0.5 rounded text-xs mr-1">{code}</code>
))}
</div>
</div>
<div>
<span className="font-medium block mb-1">Access Info:</span>
<div className="text-xs space-y-1">
{SHORTCODES_HELP.access.map(code => (
<code key={code} className="bg-purple-100 px-1 py-0.5 rounded text-xs mr-1">{code}</code>
))}
</div>
</div>
<div>
<span className="font-medium block mb-1">Consulting:</span>
<div className="text-xs space-y-1">
{SHORTCODES_HELP.consulting.map(code => (
<code key={code} className="bg-orange-100 px-1 py-0.5 rounded text-xs mr-1">{code}</code>
))}
</div>
</div>
<div>
<span className="font-medium block mb-1">Event Info:</span>
<div className="text-xs space-y-1">
{SHORTCODES_HELP.event.map(code => (
<code key={code} className="bg-pink-100 px-1 py-0.5 rounded text-xs mr-1">{code}</code>
))}
</div>
</div>
<div>
<span className="font-medium block mb-1">Bootcamp Info:</span>
<div className="text-xs space-y-1">
{SHORTCODES_HELP.bootcamp.map(code => (
<code key={code} className="bg-indigo-100 px-1 py-0.5 rounded text-xs mr-1">{code}</code>
))}
</div>
</div>
<div>
<span className="font-medium block mb-1">Company Info:</span>
<div className="text-xs space-y-1">
{SHORTCODES_HELP.company.map(code => (
<code key={code} className="bg-gray-100 px-1 py-0.5 rounded text-xs mr-1">{code}</code>
))}
</div>
</div>
<div>
<span className="font-medium block mb-1">Payment Info:</span>
<div className="text-xs space-y-1">
{SHORTCODES_HELP.payment.map(code => (
<code key={code} className="bg-red-100 px-1 py-0.5 rounded text-xs mr-1">{code}</code>
))}
</div>
</div>
</div>
<p className="text-xs">Setiap template memiliki shortcode yang relevan dengan jenis notifikasinya. Lihat detail template untuk shortcode yang tersedia.</p>
<p className="text-xs mt-3 p-2 bg-background rounded border">
<strong>Penting:</strong> Email dikirim melalui Mailketing API yang dikonfigurasi di tab Integrasi.
Pastikan API token valid dan domain pengirim sudah terdaftar di Mailketing.
@@ -724,6 +677,18 @@ export function NotifikasiTab() {
className="border-2"
/>
</div>
{/* Relevant Shortcodes for this Template */}
<div className="p-3 bg-blue-50 border border-blue-200 rounded">
<h4 className="font-semibold text-sm mb-2">Shortcodes untuk template ini:</h4>
<div className="flex flex-wrap gap-1">
{RELEVANT_SHORTCODES[template.key as keyof typeof RELEVANT_SHORTCODES]?.map(shortcode => (
<code key={shortcode} className="bg-blue-100 px-2 py-1 rounded text-xs">
{shortcode}
</code>
)) || <span className="text-xs text-gray-500">Tidak ada shortcode khusus untuk template ini</span>}
</div>
</div>
</div>
<div className="flex gap-2">