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:
@@ -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">
|
||||||
|
|||||||
Reference in New Issue
Block a user