vkashti / app / admin / layout.tsx
layout.tsx
Raw
'use client';

import { PropsWithChildren, Suspense, useRef, useEffect, useState } from 'react';
import Link from 'next/link';
import { usePathname, useSearchParams, useRouter } from 'next/navigation';
import { SignOut } from '@/utils/auth-helpers/server';
import { handleRequest } from '@/utils/auth-helpers/client';
import { createBrowserClient } from '@supabase/ssr';
import {
  FaBars,
  FaCalendarAlt,
  FaClipboardList,
  FaQuestionCircle,
  FaRegCalendarCheck,
  FaCheck,
  FaUser,
  FaSignOutAlt,
  FaUserCircle,
  FaUserTag,
  FaChair,
  FaUtensils
} from 'react-icons/fa';

const pathToTitle: { [key: string]: string } = {
  '/admin/reservations': 'Резервации',
  '/admin/schedule': 'График',
  '/admin/tasks': 'Задачи',
  '/admin/events': 'Евенти',
  '/admin/quiz': 'Куизове',
  '/admin/profiles': 'Потребители',
  '/admin/deals': 'Оферти',
  '/admin/layout': 'Бар Маси',
  '/admin/menu': 'Меню за Маси'
};

// Create a client component that uses useSearchParams
function AdminLayoutClient({ children }: PropsWithChildren) {
  const currentPath = usePathname();
  const searchParams = useSearchParams();
  const drawerCheckboxRef = useRef<HTMLInputElement>(null);
  const router = useRouter();
  const [userRole, setUserRole] = useState<string | null>(null);
  const [firstName, setFirstName] = useState<string>('');
  const [isLoading, setIsLoading] = useState(true);
  
  // Check if we're on a quiz page with a slide parameter or on the menu page
  const isQuizWithSlide = currentPath?.startsWith('/admin/quiz/') && searchParams?.has('slide');
  const isMenuPage = currentPath?.includes('/admin/menu');
  const shouldRemovePadding = isQuizWithSlide || isMenuPage;
  
  // Get the title safely
  const pageTitle = currentPath ? pathToTitle[currentPath] || 'Админ панел' : 'Админ панел';
  
  useEffect(() => {
    const fetchUserData = async () => {
      const supabase = createBrowserClient(
        process.env.NEXT_PUBLIC_SUPABASE_URL!,
        process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
      );
      const { data: { user } } = await supabase.auth.getUser();
      
      if (!user) {
        router.push('/signin');
        return;
      }

      // Fetch user role
      const { data: userRole } = await supabase
        .from('user_roles')
        .select('role')
        .eq('user_id', user.id)
        .single();
      
      // Fetch user profile
      const { data: profile } = await supabase
        .from('profiles')
        .select('first_name')
        .eq('user_id', user.id)
        .single();
      
      setUserRole(userRole?.role || null);
      setFirstName(profile?.first_name || '');
      setIsLoading(false);
    };
    fetchUserData();
  }, [router]);

  const closeDrawer = () => {
    if (drawerCheckboxRef.current) {
      drawerCheckboxRef.current.checked = false;
    }
  };

  const handleSignOut = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    const formData = new FormData(e.currentTarget);
    await handleRequest(e, SignOut, router);
  };

  const getRoleBadgeColor = (role: string | null) => {
    switch (role?.toLowerCase()) {
      case 'admin':
        return 'badge-primary';
      case 'team':
        return 'badge-secondary';
      case 'user':
        return 'badge-accent';
      default:
        return 'badge-ghost';
    }
  };
  
  return (
    <div className="admin-layout min-h-screen bg-gray-50">
      {/* Only show the navbar if not on a quiz slide page */}
      {!isQuizWithSlide && (
        <div className="navbar bg-white shadow-sm px-4 lg:hidden h-16">
          <div className="flex-1">
            <label
              htmlFor="my-drawer-2"
              className="btn btn-ghost drawer-button lg:hidden"
            >
              <FaBars className="text-gray-600" />
            </label>
            <h1 className="text-xl font-bold text-gray-800 ml-2">
              {pageTitle}
            </h1>
          </div>
        </div>
      )}
      <div className={`drawer ${isQuizWithSlide ? '' : 'lg:drawer-open'} min-h-[calc(100vh-4rem)]`}>
        <input 
          id="my-drawer-2" 
          type="checkbox" 
          className="drawer-toggle"
          ref={drawerCheckboxRef}
        />
        <div className="drawer-content">
          <main className={`${shouldRemovePadding ? 'p-0' : 'p-6'} ${shouldRemovePadding ? '' : 'lg:pl-72'} min-h-[calc(100vh-4rem)]`}>
            {!shouldRemovePadding && (
              <div className="hidden lg:block mb-6">
                <h1 className="text-2xl font-bold text-gray-800">
                  {pageTitle}
                </h1>
              </div>
            )}
            {children}
          </main>
        </div>
        {/* Hide sidebar if on quiz with slide */}
        {!shouldRemovePadding && (
          <div className="drawer-side">
            <label
              htmlFor="my-drawer-2"
              aria-label="close sidebar"
              className="drawer-overlay"
            ></label>
            <div className="menu min-h-full w-64 bg-white shadow-lg flex flex-col fixed">
              <div className="p-4 border-b border-gray-100">
                <div className="flex items-center space-x-3 mb-3">
                  <div className="w-10 h-10 rounded-full bg-orange-100 flex items-center justify-center">
                    <FaUserCircle className="text-2xl text-orange-600" />
                  </div>
                  <div className="flex-1 min-w-0">
                    <Link 
                      href="/account" 
                      className="text-sm font-medium text-gray-900 hover:text-orange-600 truncate block"
                      onClick={closeDrawer}
                    >
                      {isLoading ? 'Зареждане...' : firstName || 'Профил'}
                    </Link>
                    {userRole && (
                      <div className="flex items-center gap-1.5 mt-0.5">
                        <FaUserTag className="text-xs text-orange-600" />
                        <div className={`badge ${getRoleBadgeColor(userRole)} badge-sm font-medium`}>
                          {userRole}
                        </div>
                      </div>
                    )}
                  </div>
                  <form
                    onSubmit={handleSignOut}
                    className="flex-shrink-0"
                  >
                    <input type="hidden" name="pathName" value={currentPath || ''} />
                    <button 
                      type="submit" 
                      className="btn btn-ghost btn-sm px-2 hover:bg-orange-50"
                      title="Излез"
                    >
                      <FaSignOutAlt className="text-lg text-gray-600 hover:text-orange-600" />
                    </button>
                  </form>
                </div>
              </div>
              <div className="flex-1 overflow-y-auto py-4">
                <Suspense fallback={<></>}>
                  <MenuItems currentPath={currentPath || ''} onNavigate={closeDrawer} />
                </Suspense>
              </div>
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

function MenuItems({ 
  currentPath, 
  onNavigate 
}: { 
  currentPath: string;
  onNavigate: () => void;
}) {
  const searchParams = useSearchParams();
  const dateParam = searchParams?.get('date') || null;

  const menuItems = [
    {
      href: '/admin/reservations',
      icon: <FaRegCalendarCheck className="text-lg" />,
      label: 'Резервации'
    },
    {
      href: '/admin/schedule',
      icon: <FaClipboardList className="text-lg" />,
      label: 'График'
    },
    {
      href: '/admin/tasks',
      icon: <FaCheck className="text-lg" />,
      label: 'Задачи'
    },
    {
      href: '/admin/profiles',
      icon: <FaUser className="text-lg" />,
      label: 'Потребители'
    },
    {
      href: '/admin/quiz',
      icon: <FaQuestionCircle className="text-lg" />,
      label: 'Куизове'
    },
    {
      href: '/admin/events',
      icon: <FaCalendarAlt className="text-lg" />,
      label: 'Евенти'
    },
    {
      href: '/admin/deals',
      icon: <FaUserTag className="text-lg" />,
      label: 'Оферти'
    },
    {
      href: '/admin/layout',
      icon: <FaChair className="text-lg" />,
      label: 'Бар Маси'
    },
    {
      href: '/admin/menu',
      icon: <FaUtensils className="text-lg" />,
      label: 'Меню за Маси'
    }
  ];

  return (
    <ul className="px-3">
      {menuItems.map((item) => {
        const href = dateParam ? `${item.href}?date=${dateParam}` : item.href;
        const isActive = currentPath === item.href;
        return (
          <li key={item.href} className="mb-1">
            <Link
              className={`flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium transition-colors ${
                isActive 
                  ? 'bg-orange-100 text-orange-600' 
                  : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'
              }`}
              href={href}
              onClick={onNavigate}
            >
              {item.icon}
              {item.label}
            </Link>
          </li>
        );
      })}
    </ul>
  );
}

// Main layout component that uses Suspense
export default function AdminLayout({ children }: PropsWithChildren) {
  return (
    <Suspense fallback={<></>}>
      <AdminLayoutClient>{children}</AdminLayoutClient>
    </Suspense>
  );
}