Require login for consulting booking and add availability banner on products page

- Consulting booking now requires authentication upfront (shows login prompt to non-users)
- Added prominent consultation availability banner on products page when enabled
- Added debug logging to Layout component for branding troubleshooting
- Mobile Layout header shows responsive platform name sizing

🤖 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 00:53:51 +07:00
parent 428314d5bf
commit a8d91ee19b
3 changed files with 142 additions and 11 deletions

View File

@@ -1,29 +1,48 @@
import { ReactNode } from 'react'; import { ReactNode, useState, useEffect } from 'react';
import { Link, useNavigate } from 'react-router-dom'; import { Link, useNavigate } from 'react-router-dom';
import { useAuth } from '@/hooks/useAuth'; import { useAuth } from '@/hooks/useAuth';
import { useCart } from '@/contexts/CartContext'; import { useCart } from '@/contexts/CartContext';
import { useBranding } from '@/hooks/useBranding';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { ShoppingCart, User, LogOut, Settings } from 'lucide-react'; import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet';
import { ShoppingCart, User, LogOut, Settings, Menu, Package } from 'lucide-react';
export function Layout({ children }: { children: ReactNode }) { export function Layout({ children }: { children: ReactNode }) {
const { user, isAdmin, signOut } = useAuth(); const { user, isAdmin, signOut } = useAuth();
const { items } = useCart(); const { items } = useCart();
const branding = useBranding();
const navigate = useNavigate(); const navigate = useNavigate();
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
const handleSignOut = async () => { const handleSignOut = async () => {
await signOut(); await signOut();
navigate('/'); navigate('/');
setMobileMenuOpen(false);
}; };
const brandName = branding.brand_name || 'LearnHub';
const logoUrl = branding.brand_logo_url;
// Debug: log branding data
useEffect(() => {
console.log('Layout - branding data:', branding);
console.log('Layout - brandName:', brandName);
}, [branding, brandName]);
return ( return (
<div className="min-h-screen bg-background"> <div className="min-h-screen bg-background">
<header className="border-b-2 border-border bg-background"> <header className="border-b-2 border-border bg-background sticky top-0 z-50">
<div className="container mx-auto px-4 py-4 flex items-center justify-between"> <div className="container mx-auto px-4 py-4 flex items-center justify-between">
<Link to="/" className="text-2xl font-bold"> <Link to="/" className="text-2xl font-bold flex items-center gap-2">
LearnHub {logoUrl && (
<img src={logoUrl} alt={brandName} className="h-8 object-contain" />
)}
<span className="hidden sm:inline">{brandName}</span>
<span className="sm:hidden text-lg">{brandName}</span>
</Link> </Link>
<nav className="flex items-center gap-4"> {/* Desktop Navigation */}
<nav className="hidden md:flex items-center gap-4">
<Link to="/products" className="hover:underline font-medium"> <Link to="/products" className="hover:underline font-medium">
Products Products
</Link> </Link>
@@ -63,6 +82,76 @@ export function Layout({ children }: { children: ReactNode }) {
</Button> </Button>
</Link> </Link>
</nav> </nav>
{/* Mobile Navigation */}
<div className="md:hidden flex items-center gap-2">
<Link to="/checkout" className="relative p-2">
<ShoppingCart className="w-5 h-5" />
{items.length > 0 && (
<span className="absolute top-0 right-0 bg-primary text-primary-foreground text-xs w-4 h-4 flex items-center justify-center">
{items.length}
</span>
)}
</Link>
<Sheet open={mobileMenuOpen} onOpenChange={setMobileMenuOpen}>
<SheetTrigger asChild>
<Button variant="ghost" size="sm" className="p-2">
<Menu className="w-5 h-5" />
</Button>
</SheetTrigger>
<SheetContent side="right" className="border-l-2 border-border w-80">
<nav className="flex flex-col space-y-4 mt-8">
<Link
to="/products"
onClick={() => setMobileMenuOpen(false)}
className="flex items-center gap-3 px-4 py-3 hover:bg-muted rounded-lg text-lg font-medium"
>
<Package className="w-5 h-5" />
Products
</Link>
{user ? (
<>
<Link
to="/dashboard"
onClick={() => setMobileMenuOpen(false)}
className="flex items-center gap-3 px-4 py-3 hover:bg-muted rounded-lg text-lg font-medium"
>
Dashboard
</Link>
{isAdmin && (
<Link
to="/admin"
onClick={() => setMobileMenuOpen(false)}
className="flex items-center gap-3 px-4 py-3 hover:bg-muted rounded-lg text-lg font-medium"
>
<Settings className="w-5 h-5" />
Admin
</Link>
)}
<button
onClick={handleSignOut}
className="flex items-center gap-3 px-4 py-3 hover:bg-muted rounded-lg text-lg font-medium w-full text-left text-destructive"
>
<LogOut className="w-5 h-5" />
Logout
</button>
</>
) : (
<Link
to="/auth"
onClick={() => setMobileMenuOpen(false)}
className="flex items-center gap-3 px-4 py-3 hover:bg-muted rounded-lg text-lg font-medium"
>
<User className="w-5 h-5" />
Login
</Link>
)}
</nav>
</SheetContent>
</Sheet>
</div>
</div> </div>
</header> </header>

View File

@@ -338,6 +338,26 @@ export default function ConsultingBooking() {
); );
} }
// Require authentication to access consulting booking
if (!user) {
return (
<AppLayout>
<div className="container mx-auto px-4 py-16 text-center">
<div className="max-w-md mx-auto">
<Video className="w-16 h-16 mx-auto mb-4 text-muted-foreground" />
<h1 className="text-2xl font-bold mb-2">Login Diperlukan</h1>
<p className="text-muted-foreground mb-6">
Anda harus login untuk memesan jadwal konsultasi.
</p>
<Button onClick={() => navigate('/auth')} size="lg">
Login Sekarang
</Button>
</div>
</div>
</AppLayout>
);
}
if (!settings?.is_consulting_enabled) { if (!settings?.is_consulting_enabled) {
return ( return (
<AppLayout> <AppLayout>

View File

@@ -97,7 +97,29 @@ export default function Products() {
<AppLayout> <AppLayout>
<div className="container mx-auto px-4 py-8"> <div className="container mx-auto px-4 py-8">
<h1 className="text-4xl font-bold mb-2">Produk</h1> <h1 className="text-4xl font-bold mb-2">Produk</h1>
<p className="text-muted-foreground mb-8">Jelajahi konsultasi, webinar, dan bootcamp kami</p> <p className="text-muted-foreground mb-4">Jelajahi konsultasi, webinar, dan bootcamp kami</p>
{/* Consulting Availability Banner */}
{!loading && consultingSettings?.is_consulting_enabled && (
<div className="mb-6 p-4 bg-primary/10 border-2 border-primary rounded-lg flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="bg-primary text-primary-foreground p-2 rounded-full">
<Video className="w-5 h-5" />
</div>
<div>
<p className="font-semibold">Konsultasi Tersedia!</p>
<p className="text-sm text-muted-foreground">
Booking jadwal konsultasi 1-on-1 dengan mentor {formatIDR(consultingSettings.consulting_block_price)} / {consultingSettings.consulting_block_duration_minutes} menit
</p>
</div>
</div>
<Link to="/consulting">
<Button size="sm" className="shadow-sm">
Booking
</Button>
</Link>
</div>
)}
{loading ? ( {loading ? (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">