Fix Tiptap editor visual formatting and improve badge contrast

Tiptap Editor Improvements:
- Active toolbar buttons now use primary background (black) instead of accent (gray) for better visibility
- Added visual formatting for headings (h1: 2xl bold, h2: xl bold with proper spacing)
- Added visual styling for blockquotes (left border, italic, muted foreground)

Badge Contrast Fixes:
- Product detail page badges now use primary background (black with white text) instead of secondary/accent (gray)
- Fixed product type badge and "Anda memiliki akses" badge
- Fixed "Rekaman segera tersedia" badge

API Query Fix:
- Fixed consulting_slots 400 error by removing unsupported nested relationship filter
- Changed to filter in JavaScript after fetching data from Supabase

🤖 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-25 11:51:46 +07:00
parent 5ae1632684
commit 52190ff26d
3 changed files with 24 additions and 18 deletions

View File

@@ -449,7 +449,7 @@ export function RichTextEditor({ content, onChange, placeholder = 'Tulis konten.
variant="ghost" variant="ghost"
size="sm" size="sm"
onClick={() => editor.chain().focus().toggleBold().run()} onClick={() => editor.chain().focus().toggleBold().run()}
className={editor.isActive('bold') ? 'bg-accent' : ''} className={editor.isActive('bold') ? 'bg-primary text-primary-foreground' : ''}
> >
<Bold className="w-4 h-4" /> <Bold className="w-4 h-4" />
</Button> </Button>
@@ -458,7 +458,7 @@ export function RichTextEditor({ content, onChange, placeholder = 'Tulis konten.
variant="ghost" variant="ghost"
size="sm" size="sm"
onClick={() => editor.chain().focus().toggleItalic().run()} onClick={() => editor.chain().focus().toggleItalic().run()}
className={editor.isActive('italic') ? 'bg-accent' : ''} className={editor.isActive('italic') ? 'bg-primary text-primary-foreground' : ''}
> >
<Italic className="w-4 h-4" /> <Italic className="w-4 h-4" />
</Button> </Button>
@@ -467,7 +467,7 @@ export function RichTextEditor({ content, onChange, placeholder = 'Tulis konten.
variant="ghost" variant="ghost"
size="sm" size="sm"
onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()} onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()}
className={editor.isActive('heading', { level: 1 }) ? 'bg-accent' : ''} className={editor.isActive('heading', { level: 1 }) ? 'bg-primary text-primary-foreground' : ''}
> >
<Heading1 className="w-4 h-4" /> <Heading1 className="w-4 h-4" />
</Button> </Button>
@@ -476,7 +476,7 @@ export function RichTextEditor({ content, onChange, placeholder = 'Tulis konten.
variant="ghost" variant="ghost"
size="sm" size="sm"
onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()} onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}
className={editor.isActive('heading', { level: 2 }) ? 'bg-accent' : ''} className={editor.isActive('heading', { level: 2 }) ? 'bg-primary text-primary-foreground' : ''}
> >
<Heading2 className="w-4 h-4" /> <Heading2 className="w-4 h-4" />
</Button> </Button>
@@ -485,7 +485,7 @@ export function RichTextEditor({ content, onChange, placeholder = 'Tulis konten.
variant="ghost" variant="ghost"
size="sm" size="sm"
onClick={() => editor.chain().focus().toggleBulletList().run()} onClick={() => editor.chain().focus().toggleBulletList().run()}
className={editor.isActive('bulletList') ? 'bg-accent' : ''} className={editor.isActive('bulletList') ? 'bg-primary text-primary-foreground' : ''}
> >
<List className="w-4 h-4" /> <List className="w-4 h-4" />
</Button> </Button>
@@ -494,7 +494,7 @@ export function RichTextEditor({ content, onChange, placeholder = 'Tulis konten.
variant="ghost" variant="ghost"
size="sm" size="sm"
onClick={() => editor.chain().focus().toggleOrderedList().run()} onClick={() => editor.chain().focus().toggleOrderedList().run()}
className={editor.isActive('orderedList') ? 'bg-accent' : ''} className={editor.isActive('orderedList') ? 'bg-primary text-primary-foreground' : ''}
> >
<ListOrdered className="w-4 h-4" /> <ListOrdered className="w-4 h-4" />
</Button> </Button>
@@ -503,7 +503,7 @@ export function RichTextEditor({ content, onChange, placeholder = 'Tulis konten.
variant="ghost" variant="ghost"
size="sm" size="sm"
onClick={() => editor.chain().focus().toggleBlockquote().run()} onClick={() => editor.chain().focus().toggleBlockquote().run()}
className={editor.isActive('blockquote') ? 'bg-accent' : ''} className={editor.isActive('blockquote') ? 'bg-primary text-primary-foreground' : ''}
> >
<Quote className="w-4 h-4" /> <Quote className="w-4 h-4" />
</Button> </Button>
@@ -512,7 +512,7 @@ export function RichTextEditor({ content, onChange, placeholder = 'Tulis konten.
variant="ghost" variant="ghost"
size="sm" size="sm"
onClick={addLink} onClick={addLink}
className={editor.isActive('link') ? 'bg-accent' : ''} className={editor.isActive('link') ? 'bg-primary text-primary-foreground' : ''}
> >
<LinkIcon className="w-4 h-4" /> <LinkIcon className="w-4 h-4" />
</Button> </Button>
@@ -628,7 +628,7 @@ export function RichTextEditor({ content, onChange, placeholder = 'Tulis konten.
<div onPaste={handlePaste}> <div onPaste={handlePaste}>
<EditorContent <EditorContent
editor={editor} editor={editor}
className="prose prose-sm max-w-none p-4 min-h-[200px] focus:outline-none [&_.ProseMirror]:outline-none [&_.ProseMirror]:min-h-[180px] [&_img]:cursor-pointer [&_img.ProseMirror-selectednode]:ring-2 [&_img.ProseMirror-selectednode]:ring-primary" className="prose prose-sm max-w-none p-4 min-h-[200px] focus:outline-none [&_.ProseMirror]:outline-none [&_.ProseMirror]:min-h-[180px] [&_img]:cursor-pointer [&_img.ProseMirror-selectednode]:ring-2 [&_img.ProseMirror-selectednode]:ring-primary [&_h1]:font-bold [&_h1]:text-2xl [&_h1]:mb-4 [&_h1]:mt-6 [&_h2]:font-bold [&_h2]:text-xl [&_h2]:mb-3 [&_h2]:mt-5 [&_blockquote]:border-l-4 [&_blockquote]:border-primary [&_blockquote]:pl-4 [&_blockquote]:italic [&_blockquote]:text-muted-foreground [&_blockquote]:my-4"
/> />
</div> </div>
{uploading && ( {uploading && (

View File

@@ -267,7 +267,7 @@ export default function ProductDetail() {
Gabung Webinar Gabung Webinar
</a> </a>
</Button> </Button>
) : <Badge className="bg-secondary">Rekaman segera tersedia</Badge>; ) : <Badge className="bg-primary text-primary-foreground">Rekaman segera tersedia</Badge>;
case 'bootcamp': case 'bootcamp':
return ( return (
<Button onClick={() => navigate(`/bootcamp/${product.slug}`)} size="lg" className="shadow-sm"> <Button onClick={() => navigate(`/bootcamp/${product.slug}`)} size="lg" className="shadow-sm">
@@ -346,8 +346,8 @@ export default function ProductDetail() {
<div> <div>
<h1 className="text-4xl font-bold mb-2">{product.title}</h1> <h1 className="text-4xl font-bold mb-2">{product.title}</h1>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Badge className="bg-secondary capitalize">{product.type}</Badge> <Badge className="bg-primary text-primary-foreground capitalize">{product.type}</Badge>
{hasAccess && <Badge className="bg-accent">Anda memiliki akses</Badge>} {hasAccess && <Badge className="bg-primary text-primary-foreground">Anda memiliki akses</Badge>}
</div> </div>
</div> </div>
<div className="text-right"> <div className="text-right">

View File

@@ -66,18 +66,24 @@ export default function MemberDashboard() {
qr_expires_at qr_expires_at
) )
`) `)
.eq('orders.payment_status', 'pending')
.eq('status', 'pending_payment') .eq('status', 'pending_payment')
.gt('orders.qr_expires_at', new Date().toISOString())
.order('created_at', { ascending: false }); .order('created_at', { ascending: false });
if (!error && data) { if (!error && data) {
// Filter in JavaScript: only include slots where order is pending AND not expired
const now = new Date().toISOString();
const validSlots = data.filter((item: any) =>
item.orders?.payment_status === 'pending' &&
item.orders?.qr_expires_at &&
item.orders.qr_expires_at > now
);
// Get unique order IDs // Get unique order IDs
const uniqueOrders = Array.from( const uniqueOrders = Array.from(
new Set(data.map((item: any) => item.order_id)) new Set(validSlots.map((item: any) => item.order_id))
).map((orderId) => { ).map((orderId) => {
// Find the corresponding order data // Find the corresponding order data
const orderData = data.find((item: any) => item.order_id === orderId); const orderData = validSlots.find((item: any) => item.order_id === orderId);
return { return {
order_id: orderId, order_id: orderId,
qr_expires_at: (orderData as any)?.orders?.qr_expires_at || '' qr_expires_at: (orderData as any)?.orders?.qr_expires_at || ''