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