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 { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { Switch } from '@/components/ui/switch'; import { Switch } from '@/components/ui/switch';
import { Textarea } from '@/components/ui/textarea';
import { Alert, AlertDescription } from '@/components/ui/alert'; import { Alert, AlertDescription } from '@/components/ui/alert';
import { RichTextEditor } from '@/components/RichTextEditor'; import { RichTextEditor } from '@/components/RichTextEditor';
import { toast } from '@/hooks/use-toast'; import { toast } from '@/hooks/use-toast';
@@ -24,25 +23,14 @@ interface NotificationTemplate {
last_payload_example: Record<string, unknown> | null; last_payload_example: Record<string, unknown> | null;
} }
const SHORTCODES_HELP = { const RELEVANT_SHORTCODES = {
// User information 'payment_success': ['{nama}', '{email}', '{order_id}', '{tanggal_pesanan}', '{total}', '{metode_pembayaran}', '{produk}', '{link_akses}'],
common: ['{nama}', '{email}'], 'access_granted': ['{nama}', '{email}', '{produk}', '{link_akses}', '{username_akses}', '{password_akses}', '{kadaluarsa_akses}'],
// Order information 'order_created': ['{nama}', '{email}', '{order_id}', '{tanggal_pesanan}', '{total}', '{metode_pembayaran}', '{produk}'],
orders: ['{order_id}', '{tanggal_pesanan}', '{total}', '{metode_pembayaran}', '{status_pesanan}', '{invoice_url}'], 'payment_reminder': ['{nama}', '{email}', '{order_id}', '{tanggal_pesanan}', '{total}', '{metode_pembayaran}', '{batas_pembayaran}', '{jumlah_pembayaran}', '{bank_tujuan}', '{nomor_rekening}'],
// Product information 'consulting_scheduled': ['{nama}', '{email}', '{tanggal_konsultasi}', '{jam_konsultasi}', '{durasi_konsultasi}', '{link_meet}', '{jenis_konsultasi}', '{topik_konsultasi}'],
products: ['{produk}', '{kategori_produk}', '{harga_produk}', '{deskripsi_produk}'], 'event_reminder': ['{nama}', '{email}', '{judul_event}', '{tanggal_event}', '{jam_event}', '{link_event}', '{lokasi_event}', '{kapasitas_event}'],
// Access information 'bootcamp_progress': ['{nama}', '{email}', '{judul_bootcamp}', '{progres_bootcamp}', '{modul_selesai}', '{modul_selanjutnya}', '{link_progress}'],
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 DEFAULT_TEMPLATES: { key: string; name: string; defaultSubject: string; defaultBody: string }[] = [ 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('Templates data:', templatesData);
console.log('Template count:', templatesData?.length);
if (templatesData && templatesData.length > 0) { if (templatesData && templatesData.length > 0) {
console.log('Setting templates from database:', templatesData.length); console.log('Setting templates from database:', templatesData.length);
setTemplates(templatesData); // 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 { } else {
console.log('No templates found, seeding default templates...'); console.log('No templates found, seeding default templates...');
// Seed default templates if none exist // 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 () => { const seedTemplates = async () => {
try { try {
console.log('Seeding default templates...'); console.log('Seeding default templates...');
@@ -550,6 +563,19 @@ export function NotifikasiTab() {
Gunakan Mailketing API untuk pengiriman email yang andal. Gunakan Mailketing API untuk pengiriman email yang andal.
</AlertDescription> </AlertDescription>
</Alert> </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> </CardContent>
</Card> </Card>
@@ -564,80 +590,7 @@ export function NotifikasiTab() {
<CardContent className="space-y-4"> <CardContent className="space-y-4">
<div className="text-sm text-muted-foreground p-3 bg-muted rounded-md space-y-2"> <div className="text-sm text-muted-foreground p-3 bg-muted rounded-md space-y-2">
<p className="font-medium">Shortcode yang tersedia:</p> <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"> <p className="text-xs">Setiap template memiliki shortcode yang relevan dengan jenis notifikasinya. Lihat detail template untuk shortcode yang tersedia.</p>
<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 mt-3 p-2 bg-background rounded border"> <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. <strong>Penting:</strong> Email dikirim melalui Mailketing API yang dikonfigurasi di tab Integrasi.
Pastikan API token valid dan domain pengirim sudah terdaftar di Mailketing. Pastikan API token valid dan domain pengirim sudah terdaftar di Mailketing.
@@ -724,6 +677,18 @@ export function NotifikasiTab() {
className="border-2" className="border-2"
/> />
</div> </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>
<div className="flex gap-2"> <div className="flex gap-2">