Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
311 changes: 251 additions & 60 deletions app/[locale]/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,23 @@ import { userAgent } from 'next/server';
import { Suspense } from 'react';
import { BsPersonFill } from 'react-icons/bs';
import { FaMagnifyingGlass } from 'react-icons/fa6';
import { MdArrowBackIosNew } from 'react-icons/md';

import { Button, HamburgerButton } from '~/components/buttons';
import {
Button,
HamburgerButton,
NavButton,
NavStyleSwitcher,
SwitchNavButton,
} from '~/components/buttons';
import { CtrlLink } from '~/components/link';
import LocaleSwitcher from '~/components/locale-switcher';
import MaybeLink from '~/components/maybe-link';
import {
NavigationMenu,
NavigationMenuCustomListItem,
NavigationMenuList,
} from '~/components/ui';
import { getTranslations } from '~/i18n/translations';
import { cn } from '~/lib/utils';
import { getServerAuthSession } from '~/server/auth';
Expand All @@ -22,42 +34,129 @@ export default async function Header({ locale }: { locale: string }) {
const isMacOS = agent.os.name === 'Mac OS';

const items = [
{ label: text.institute, href: 'institute' },
{ label: text.academics, href: 'academics' },
{
label: text.institute,
href: 'institute',
listItems: [
{
title: 'Administration',
href: '/institute/administration',
description: 'Meet the leadership team guiding our institution.',
},
{
title: 'Sections',
href: '/institute/sections',
description:
'Explore the various sections that support campus life and academics.',
},
{
title: 'Campus Infrastructure',
href: '/institute/campus-infra',
description:
'Discover the state-of-the-art facilities and infrastructure on campus.',
},
],
},
{
label: text.academics,
href: 'academics',
listItems: [
{
title: 'Departments',
href: '/academics/departments',
description:
'Explore the diverse academic departments and their offerings.',
},
{
title: 'Programmes',
href: '/academics/programmes',
description:
'Discover our range of undergraduate and postgraduate programmes.',
},
{
title: 'Courses & Curricula',
href: '/academics/curricula',
description: 'Browse through the detailed list of courses available.',
},
{
title: 'Convocation',
href: '/academics/convocation',
description: 'Get information on upcoming convocation ceremonies.',
},
{
title: 'Awards',
href: '/academics/awards',
description:
'Recognizing excellence in academics, research, and beyond.',
},
{
title: 'Scholarship',
href: '/academics/scholarships',
description:
'Learn about scholarships, eligibility, and application details.',
},
{
title: 'Academic Notifications',
href: '/academics/notifications',
description:
'Stay updated with the latest academic announcements and deadlines.',
},
],
},
{ label: text.faculty, href: 'faculty-and-staff' },
{ label: text.placement, href: 'training-and-placement' },
{ label: text.alumni, href: 'alumni' },
// { label: text.alumni, href: 'alumni' },
{ label: text.activities, href: 'student-activities' },
{
label: text.alumni,
href: 'https://nitkkraa.org',
isExternal: true,
},
{
label: text.research,
href: 'research',
},
];

return (
<header className="header-sticky-ness sticky top-0 z-nav min-w-full bg-background">
<nav
className={cn(
'container flex justify-between',
'gap-4 xl:gap-6 2xl:gap-8',
'sm:gap-4 xl:gap-6 2xl:gap-8',
'py-2 sm:py-4 md:py-6'
)}
>
<Link href={`/${locale}`}>
<Image
alt={text.logo}
className="rounded-md bg-neutral-50 p-[6px]"
height={40}
width={40}
className="rounded-md bg-neutral-50"
height={45}
width={45}
src="assets/nitlogo.png"
/>
</Link>

<ol className={cn('hidden grow lg:flex', 'gap-4 xl:gap-5 2xl:gap-6')}>
{items.map(({ label, href }, index) => (
<li className="my-auto min-h-fit" key={index}>
<Link href={`/${locale}/${href}`} prefetch>
{label}
</Link>
</li>
))}
</ol>
<NavigationMenu>
<NavigationMenuList
className={cn('hidden grow lg:flex', 'gap-4 xl:gap-5 2xl:gap-6')}
>
{items.map(({ label, href, isExternal, listItems }, index) => (
<NavigationMenuCustomListItem
key={index}
triggerName={label}
locale={locale}
isExternal={isExternal}
listItems={listItems}
href={href}
imageDetails={{
src: `${href}/image01.jpg`,
alt: label,
href: '/' + href,
}}
/>
))}
</NavigationMenuList>
</NavigationMenu>

<ol className="inline-flex h-10 gap-2">
<li className="flex h-full rounded-xl border border-neutral-500 bg-neutral-50">
Expand Down Expand Up @@ -143,48 +242,74 @@ export default async function Header({ locale }: { locale: string }) {
</Suspense>
</li>
<li className="z-30 font-semibold lg:hidden">
<nav className="relative flex h-0">
<HamburgerButton
className="peer sticky z-40 size-10 transition-colors aria-expanded:bg-transparent"
data-dropdownignore={true}
/>
<nav className="flex h-0">
<HamburgerButton className="peer sticky z-40 size-10 transition-colors aria-expanded:bg-transparent" />
<aside
className={cn(
'absolute -right-2 -top-2',
'hidden peer-aria-expanded:flex',
'border border-primary-500 bg-background',
'w-72 max-w-[calc(100vw-1rem)] flex-col gap-y-4 rounded-md p-6'
'fixed left-0 top-0',
'invisible opacity-0 peer-aria-expanded:visible peer-aria-expanded:opacity-100',
'transition-all',
'translate-y-[-100%] peer-aria-expanded:translate-y-0',
'bg-background',
'h-screen w-screen '
)}
data-dropdownignore={true}
>
<ul
className="space-y-4 text-base font-semibold"
data-dropdownignore
>
{items.map(({ label, href }, index) => (
<li key={index} className="w-fit">
<Button
asChild
className="text-left text-shade-dark"
variant="link"
>
<Link href={`/${locale}/${href}`}>{label}</Link>
</Button>
</li>
))}
</ul>
<hr className="opacity-50" data-dropdownignore={true} />
<Suspense>
<AuthAction
locale={locale}
mobile
text={{
alt: text.profile.alt,
login: text.login,
view: text.profile.view,
}}
/>
</Suspense>
<NavStyleSwitcher />
<main className="container flex h-dvh max-h-dvh flex-col xl:gap-6 2xl:gap-8">
<header className="mb-6 mt-2 h-10 sm:mb-8 sm:mt-4 md:mb-10 md:mt-6">
<SwitchNavButton
className="nav-column-academics nav-column-institute invisible my-auto aspect-square !h-full text-xl font-bold"
column="default"
variant="link"
>
<MdArrowBackIosNew className="size-8" />
</SwitchNavButton>
</header>
<ul className="nav-column-default space-y-4 text-base font-semibold">
{items.map(
({ isExternal, label, href, listItems }, index) => (
<li key={index} className="w-fit">
{listItems ? (
<SwitchNavButton
className="text-left text-shade-dark"
column={href}
text={label + '>'}
variant="link"
/>
) : (
<NavButton
asChild
className="text-left text-shade-dark"
variant="link"
>
<Link
href={isExternal ? href : `/${locale}/${href}`}
>
{label}
</Link>
</NavButton>
)}
</li>
)
)}
</ul>
<MobileSubNavMenu locale={locale} {...items[0]} />
<MobileSubNavMenu locale={locale} {...items[1]} />
<footer className="mt-auto flex flex-col gap-y-4 py-4">
<hr className="opacity-50" />
<Suspense>
<AuthAction
locale={locale}
mobile
text={{
alt: text.profile.alt,
login: text.login,
view: text.profile.view,
}}
/>
</Suspense>
</footer>
</main>
</aside>
</nav>
</li>
Expand Down Expand Up @@ -255,15 +380,15 @@ const AuthAction = async ({
);
} else {
return mobile ? (
<Button asChild className="py-2 text-center">
<NavButton asChild className="py-2 text-center">
<Link href={`/${locale}/login`}>{text.login}</Link>
</Button>
</NavButton>
) : (
<Button asChild className="h-full w-16 xl:w-20">
<NavButton asChild className="h-full w-16 xl:w-20">
<Link href={`/${locale}/login`} prefetch scroll={false}>
{text.login}
</Link>
</Button>
</NavButton>
);
}
};
Expand Down Expand Up @@ -301,3 +426,69 @@ const ProfileImage = ({
</Button>
);
};

const MobileSubNavMenu = ({
label,
href,
listItems,
locale,
}: {
label: string;
href: string;
listItems?: {
title: string;
href: string;
description: string;
}[];
locale: string;
}) => {
if (!listItems) return null;
return (
<article
className={`nav-column-${href} invisible flex h-0 flex-col overflow-y-auto`}
>
<NavButton asChild variant="icon">
<Link
href={`/${locale}/${href}`}
className="group relative mb-6 !flex aspect-[5/2] min-h-20 select-none !flex-col justify-end overflow-hidden rounded-xl no-underline outline-none"
>
<Image
className="absolute inset-0 z-0 h-full w-full object-cover transition-transform duration-500 ease-in-out group-hover:scale-125"
alt={href}
src={`${href}/image01.jpg`}
width={0}
height={0}
/>
<section className="relative z-30 flex h-full w-full flex-col justify-end rounded-xl bg-gradient-to-b from-primary-500/0 to-primary-500 p-2 focus:shadow-md">
<h2 className="!mb-0 origin-bottom-left text-shade-light transition-transform duration-500 ease-in-out group-hover:scale-125">
{label + '→'}
</h2>
</section>
</Link>
</NavButton>
<ul
className={'grid w-full gap-2 overflow-y-auto overflow-x-hidden pb-1'}
>
{listItems.map(({ title, description, href }, index) => (
<li key={index}>
<NavButton variant="icon" asChild>
<Link
className={cn(
'group !flex max-w-full origin-left select-none flex-col !items-start space-y-1 !whitespace-break-spaces rounded-xl px-1 py-3 leading-none no-underline outline-none transition-colors duration-500 ease-in-out hover:bg-neutral-50 focus:bg-neutral-50'
)}
href={`/${locale}/${href}`}
>
<h4 className="mb-0 origin-bottom-left font-sans font-semibold leading-none text-shade-dark transition-colors transition-transform group-hover:scale-105 group-hover:text-primary-500 group-focus:text-primary-500">
{title}
</h4>
<p className="line-clamp-3 origin-top-left text-sm leading-snug !text-neutral-600 transition-colors transition-transform group-hover:scale-105 group-hover:text-primary-500 group-focus:text-primary-500">
{description}
</p>
</Link>
</NavButton>
</li>
))}
</ul>
</article>
);
};
Loading