File "StudentHeader.tsx"

Full Path: /home/trinadezambia/public_html/student_panel/src/components/ui/pages/dashboard/StudentHeader.tsx
File size: 13.63 KB
MIME-type: text/x-java
Charset: utf-8

'use client';

import { useState, useRef, useEffect } from 'react';
import {
  BiMenu,
  BiChevronDown,
  BiGlobe,
  BiLogOut,
  BiUserCircle,
  BiLock,
  BiX,
} from 'react-icons/bi';
import { cn } from '@/components/lib/utils';
import NotificationDropdown from './NotificationDropdown';
import {
  Dialog,
  DialogContent,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
  DialogClose,
} from '@/components/ui/dialog';
import Image from 'next/image';
import Link from 'next/link';
import ChangePasswordModal from './ChangePasswordModal';
import LogoutModal from './LogoutModal';
import { useStudentLogout } from '@/lib/api/student';
import { useRouter } from 'next/navigation';
import { useSelector } from 'react-redux';
import { RootState } from '@/components/store';
import { useLanguage } from '@/components/hooks/useLanguage';
import { useTranslate } from '@/components/hooks/useTranslate';
import { useGetSchoolSettings } from '@/lib/api/student/queryHooks';

interface StudentHeaderProps {
  onMenuClick: () => void;
  isMobile?: boolean;
}

export default function StudentHeader({
  onMenuClick,
  isMobile = false,
}: StudentHeaderProps) {
  const [selectedLanguage, setSelectedLanguage] = useState('English');
  const [isLanguageModalOpen, setIsLanguageModalOpen] = useState(false);

  // Change password modal state
  const [isChangePasswordModalOpen, setIsChangePasswordModalOpen] =
    useState(false);

  // Logout modal state
  const [isLogoutModalOpen, setIsLogoutModalOpen] = useState(false);

  // User profile dropdown state
  const [isProfileOpen, setIsProfileOpen] = useState(false);
  const [isProfileVisible, setIsProfileVisible] = useState(false);
  const profileDropdownRef = useRef<HTMLDivElement>(null);
  const profileTriggerRef = useRef<HTMLDivElement>(null);
  const hoverTimeoutRef = useRef<NodeJS.Timeout | null>(null);

  // Logout hook and router for handling logout
  const { logout } = useStudentLogout();
  const router = useRouter();

  // Get user data from Redux store
  const user = useSelector((state: RootState) => state.studentAuth.user);

  // Get language state and translate function from Redux
  const { changeLanguage, currentLanguageName, languages } = useLanguage();
  const translate = useTranslate();
  // Load school settings once so we can swap the logo whenever backend updates it
  const { data: schoolSettings, isLoading: isLoadingLogo } =
    useGetSchoolSettings();
  const fallbackLogo = '/assets/images/common/logo.png';
  const dynamicLogo = schoolSettings?.data?.settings?.horizontal_logo;
  const resolvedLogo =
    typeof dynamicLogo === 'string' && dynamicLogo.trim().length > 0
      ? dynamicLogo.trim()
      : fallbackLogo;

  // Initialize selected language from Redux store
  useEffect(() => {
    setSelectedLanguage(currentLanguageName);
  }, [currentLanguageName]);

  // Handle language selection - update Redux store and close modal
  const handleLanguageSelect = (languageName: string) => {
    // Find the language code from the language name
    const selectedLang = languages.find((lang) => lang.name === languageName);
    if (selectedLang) {
      // Update language in Redux store
      changeLanguage(selectedLang.code);
      setSelectedLanguage(languageName);
    }
    setIsLanguageModalOpen(false); // Close modal after selection
  };

  // Handle logout - clears Redux state, query cache, and redirects to login
  const handleLogout = () => {
    // Call logout function to clear Redux state and query cache
    logout();

    // Close the logout modal
    setIsLogoutModalOpen(false);

    // Redirect to student login page
    router.push('/student/auth/login');
  };

  // Handle profile dropdown hover
  const handleProfileMouseEnter = () => {
    if (hoverTimeoutRef.current) {
      clearTimeout(hoverTimeoutRef.current);
    }
    setIsProfileOpen(true);
    setTimeout(() => setIsProfileVisible(true), 10);
  };

  const handleProfileMouseLeave = () => {
    hoverTimeoutRef.current = setTimeout(() => {
      setIsProfileVisible(false);
      setTimeout(() => setIsProfileOpen(false), 150);
    }, 150);
  };

  // Handle click outside to close profile dropdown
  useEffect(() => {
    const handleClickOutside = (event: MouseEvent) => {
      if (
        profileDropdownRef.current &&
        !profileDropdownRef.current.contains(event.target as Node) &&
        profileTriggerRef.current &&
        !profileTriggerRef.current.contains(event.target as Node)
      ) {
        setIsProfileVisible(false);
        setTimeout(() => setIsProfileOpen(false), 150);
      }
    };

    if (isProfileOpen) {
      document.addEventListener('mousedown', handleClickOutside);
    }

    return () => {
      document.removeEventListener('mousedown', handleClickOutside);
      if (hoverTimeoutRef.current) {
        clearTimeout(hoverTimeoutRef.current);
      }
    };
  }, [isProfileOpen]);

  return (
    <div className="bg-white border-b border-gray-200 px-3 sm:px-4 lg:px-6 py-3 md:py-4 flex items-center justify-between sticky top-0 z-40">
      {/* Left Section - Menu and Logo */}
      {/* Using gap instead of space-x for RTL support */}
      <div className="flex items-center gap-2 md:gap-3 lg:gap-4">
        <button
          onClick={onMenuClick}
          className="p-1.5 md:p-2 hover:bg-gray-100 rounded-[4px] transition-colors border-[1px] border-[#EAEAEA]"
        >
          <BiMenu className="w-5 h-5 md:w-6 md:h-6 text-gray-600" />
        </button>

        {/* Mobile Logo - Show on mobile and tablet */}
        {isMobile && (
          <div className="flex items-center">
            <div className="w-[90px] h-[32px] sm:w-[100px] sm:h-[35px]">
              {isLoadingLogo ? (
                // Loading skeleton for compact mobile logo
                <div className="w-full h-full bg-gray-200 rounded animate-pulse" />
              ) : (
                <Image
                  src={resolvedLogo}
                  alt="Logo"
                  width={0}
                  height={0}
                  className="w-full h-full object-contain"
                  priority
                />
              )}
            </div>
          </div>
        )}
      </div>

      {/* Right Section - User Info and Controls */}
      {/* Using gap instead of space-x for RTL support */}
      <div className="flex items-center gap-1.5 sm:gap-2 lg:gap-4">
        {/* Language Selector Modal - Desktop and Tablet */}
        <div className="hidden md:block">
          <Dialog
            open={isLanguageModalOpen}
            onOpenChange={setIsLanguageModalOpen}
          >
            <DialogTrigger asChild>
              <div className="flex items-center gap-1.5 lg:gap-2 cursor-pointer hover:bg-gray-50 px-2 lg:px-3 py-1.5 lg:py-2 rounded-lg transition-colors border border-gray-300 bg-white hover:border-gray-400">
                <BiGlobe className="w-4 h-4 text-gray-600 flex-shrink-0" />
                <span className="text-xs lg:text-sm font-medium text-gray-900 ">
                  {selectedLanguage}
                </span>
                {/* Show only icon on tablets, full text on large screens */}
                <BiChevronDown className="w-3.5 h-3.5 lg:w-4 lg:h-4 text-gray-500 " />
              </div>
            </DialogTrigger>
            <DialogContent className="sm:max-w-lg" showCloseButton={false}>
              <DialogHeader className="flex flex-row items-center justify-between">
                <DialogTitle className="text-left text-xl font-bold text-gray-900">
                  {translate('chooseYourLanguage')}
                </DialogTitle>
                <DialogClose asChild>
                  <button className="p-2 bg-gray-100 rounded transition-colors border-[1px] border-[#EAEAEA]">
                    <BiX className="w-5 h-5 text-gray-400" />
                  </button>
                </DialogClose>
              </DialogHeader>
              <div className="space-y-2 mt-6">
                {languages.map((language) => (
                  <button
                    key={language.code}
                    onClick={() => handleLanguageSelect(language.name)}
                    className={`w-full flex items-center gap-3 p-3 rounded-lg border transition-colors text-left ${
                      selectedLanguage === language.name
                        ? 'border-gray-300 bg-gray-100'
                        : 'border-gray-200 hover:bg-gray-50 hover:border-gray-300'
                    }`}
                  >
                    <span className="text-lg">{language.flag}</span>
                    <span className="text-sm font-medium text-gray-900">
                      {language.name}
                    </span>
                  </button>
                ))}
              </div>
            </DialogContent>
          </Dialog>
        </div>

        {/* Notifications */}
        <NotificationDropdown />

        {/* vertical divider - Hidden on mobile and tablet */}
        <div className="hidden lg:block h-10 w-[1px] bg-[#EAEAEA]"></div>

        {/* User Profile */}
        <div className="relative">
          {/* Trigger */}
          <div
            ref={profileTriggerRef}
            onMouseEnter={handleProfileMouseEnter}
            onMouseLeave={handleProfileMouseLeave}
            className="flex items-center gap-1.5 sm:gap-2 lg:gap-3 cursor-pointer hover:bg-gray-50 rounded-[4px] transition-colors p-1 md:p-1.5 lg:p-2"
          >
            <div className="border-[2px] border-[#EAEAEA] rounded-full p-0.5 md:p-1">
              <div className="w-6 h-6 md:w-7 md:h-7 lg:w-8 lg:h-8 rounded-full flex items-center justify-center overflow-hidden">
                <Image
                  src={user?.image || resolvedLogo}
                  alt="Profile"
                  width={0}
                  height={0}
                  className="w-full h-full object-cover"
                  priority
                />
              </div>
            </div>

            {/* User info - Show only on large screens (desktop) */}
            <div className="hidden lg:flex flex-col">
              <span className="text-sm font-medium text-gray-800 max-w-[150px] truncate">
                {user?.first_name && user?.last_name
                  ? `${user.first_name} ${user.last_name}`
                  : 'Student'}
              </span>
              <span className="text-sm font-normal text-gray-800 flex items-center">
                {/* Class section with truncate - max width ~20 characters */}
                <span className="max-w-[100px] truncate">
                  {user?.class_section?.full_name || 'N/A'}
                </span>
                {/* vertical divider */}
                <span className="w-[1px] h-[15px] bg-gray-300 mx-2 flex items-center justify-center flex-shrink-0"></span>
                <span className="whitespace-nowrap">
                  {translate('rollNo')}: {user?.roll_number || 'N/A'}
                </span>
              </span>
            </div>
            <BiChevronDown className="text-base md:text-lg lg:text-xl text-white bg-(--primary-color) rounded-full p-0.5 md:p-1 flex-shrink-0" />
          </div>

          {/* Dropdown Content */}
          {isProfileOpen && (
            <div
              ref={profileDropdownRef}
              onMouseEnter={() => {
                if (hoverTimeoutRef.current) {
                  clearTimeout(hoverTimeoutRef.current);
                }
              }}
              onMouseLeave={handleProfileMouseLeave}
              className={cn(
                'absolute top-full ltr:right-0 rtl:left-0 z-50 mt-1 w-56 overflow-hidden rounded-md border bg-white shadow-md',
                'transition-all duration-200 ease-in-out',
                isProfileVisible
                  ? 'opacity-100 scale-100 translate-y-0'
                  : 'opacity-0 scale-95 -translate-y-1',
              )}
            >
              <div className="text-sm font-normal py-3">
                {/* <Link
                  href="/student/dashboard"
                  className="p-3 hover:bg-gray-100 cursor-pointer flex items-center"
                >
                  <BiHomeAlt2 className="ltr:mr-2 rtl:ml-2 h-5 w-5" />
                  <span>{translate('goBackToWebsite')}</span>
                </Link> */}
                <div className="h-px bg-gray-50"></div>
                <Link
                  href="/student/profile"
                  className="p-3 hover:bg-gray-100 cursor-pointer flex items-center"
                >
                  <BiUserCircle className="ltr:mr-2 rtl:ml-2 h-5 w-5" />
                  <span>{translate('myProfile')}</span>
                </Link>
                <div className="h-px bg-gray-50"></div>
                <button
                  onClick={() => setIsChangePasswordModalOpen(true)}
                  className="w-full p-3 hover:bg-gray-100 cursor-pointer flex items-center text-left"
                >
                  <BiLock className="ltr:mr-2 rtl:ml-2 h-5 w-5" />
                  <span>{translate('changePassword')}</span>
                </button>
                <div className="h-px bg-gray-50"></div>
                <button
                  onClick={() => setIsLogoutModalOpen(true)}
                  className="w-full p-3 hover:bg-gray-100 cursor-pointer flex items-center text-left"
                >
                  <BiLogOut className="ltr:mr-2 rtl:ml-2 h-5 w-5" />
                  <span>{translate('logOut')}</span>
                </button>
              </div>
            </div>
          )}
        </div>
      </div>

      {/* Change Password Modal */}
      <ChangePasswordModal
        open={isChangePasswordModalOpen}
        onOpenChange={setIsChangePasswordModalOpen}
      />

      {/* Logout Confirmation Modal */}
      <LogoutModal
        open={isLogoutModalOpen}
        onOpenChange={setIsLogoutModalOpen}
        onConfirm={handleLogout}
      />
    </div>
  );
}