Skip to content

Commit

Permalink
Feat: Added navbar and footer
Browse files Browse the repository at this point in the history
  • Loading branch information
magnusgiverin committed Oct 9, 2024
1 parent 6ec37f3 commit e885fb3
Show file tree
Hide file tree
Showing 13 changed files with 421 additions and 51 deletions.
Binary file added frontend/public/Kartai-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/Kartai-logo_white.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions frontend/src/app/_components/Breakline.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import React from 'react';

const BreakLine: React.FC = () => {
return <hr className="border-kartAI-blue mx-5" />;
};

export default BreakLine;
96 changes: 96 additions & 0 deletions frontend/src/app/_components/Dropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
'use client';
import { useEffect, useRef, useState } from 'react';
import ReactDOM from 'react-dom';
import Icons from './Icons';
import shortcuts from '../data/shortcuts'

const Dropdown = () => {
const [isOpen, setIsOpen] = useState(false);
const [openedNavbarSection, setOpenedNavbarSection] = useState<number | null>(null); // Track which section is opened
const dropdownRef = useRef<HTMLDivElement>(null);

const toggleDropdown = () => {
setIsOpen(!isOpen);
};

const closeDropdown = () => {
setIsOpen(false);
};

const handleClick = (index: number) => {
setOpenedNavbarSection(index === openedNavbarSection ? null : index); // Close if clicked again
};

useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
closeDropdown();
}
};
// Attach the event listener when the component mounts
document.addEventListener('click', handleClickOutside);

// Detach the event listener when the component unmounts
return () => {
document.removeEventListener('click', handleClickOutside);
};
}, []);

return (
<>
{isOpen && ReactDOM.createPortal(
<div className="fixed inset-0 bg-black bg-opacity-75 z-10"></div>,
document.body
)}
<div className="px-4 py-2 top-0 relative group text-kartAI-blue flex z-20" ref={dropdownRef}>
<button onClick={toggleDropdown}>
{isOpen ?
<Icons name="Cross" /> : <Icons name="Dropdown" />
}
</button>
{/* Dropdown content */}
{isOpen && (
<div className='absolute right-0 mt-11 w-screen bg-white z-30'>
{shortcuts.map((shortcut, index) => {
const isActive = index === openedNavbarSection;
return (
<div key={index} className="border-b border-gray-200">
<button
onClick={() => handleClick(index)}
className={`${isActive ? 'font-bold' : ''} tracking-[0.08em] block px-4 py-3 gap-2 text-sm text-black flex flex-row justify-between items-center`}
>
<h3 className='flex whitespace-pre-wrap items-center'>{shortcut.header.toUpperCase()}</h3>
<Icons name={isActive ? 'ArrowUp_sm' : 'ArrowRight_sm'} />
</button>
{/* Subgroups: reveal when active */}
<div
className={`overflow-hidden transition-[max-height] duration-300 ease-in-out ${isActive ? 'max-h-screen' : 'max-h-0'}`}
>
{isActive && shortcut.subgroups.map((subgroup, subgroupIndex) => (
<div key={subgroupIndex} className="px-6 py-4">
<h3 className="text-lg font-bold mb-2">{subgroup.title}</h3>
<div className={`flex flex-wrap flex-col`}>
{subgroup.links.map((link, linkIndex) => (
<a
key={linkIndex}
href={link.url}
className="block text-sm text-black hover:text-gray-600 py-1 transition-all duration-300 transform hover:scale-105"
>
{link.label}
</a>
))}
</div>
</div>
))}
</div>
</div>
);
})}
</div>
)}
</div>
</>
);
};

export default Dropdown;
11 changes: 11 additions & 0 deletions frontend/src/app/_components/Footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const Footer = () => {
return (
<footer className="h-12 shadow-inner">
<div className="h-full flex justify-center items-center">
<p className="text-gray-500 text-sm">© {new Date().getFullYear()} KartAI</p>
</div>
</footer>
);
};

export default Footer;
82 changes: 82 additions & 0 deletions frontend/src/app/_components/Icons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
'use client';
import React from 'react';

interface IconsProps {
name: string;
}

const Icons: React.FC<IconsProps> = ({ name }) => {
// Define SVG paths for different icons
const iconPaths: Record<string, React.JSX.Element> = {
Dropdown:
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="md:size-6 size-8">
<path strokeLinecap="round" strokeLinejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />

</svg>,
Cross:
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="md:size-6 size-8">
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18 18 6M6 6l12 12" />
</svg>,
Om:
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth="1.5" stroke="currentColor" className="size-5">
<path strokeLinecap="round" strokeLinejoin="round" d="M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 5.25h.008v.008H12v-.008Z" />
</svg>,
Formidling:
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth="1.5" stroke="currentColor" className="size-5">
<path strokeLinecap="round" strokeLinejoin="round" d="M15 19.128a9.38 9.38 0 0 0 2.625.372 9.337 9.337 0 0 0 4.121-.952 4.125 4.125 0 0 0-7.533-2.493M15 19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 0 1 8.624 21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0 1 11.964-3.07M12 6.375a3.375 3.375 0 1 1-6.75 0 3.375 3.375 0 0 1 6.75 0Zm8.25 2.25a2.625 2.625 0 1 1-5.25 0 2.625 2.625 0 0 1 5.25 0Z" />
</svg>,
Arrangementer:
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth="1.5" stroke="currentColor" className="md:size-6 size-8">
<path strokeLinecap="round" strokeLinejoin="round" d="M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 0 1 2.25-2.25h13.5A2.25 2.25 0 0 1 21 7.5v11.25m-18 0A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75m-18 0v-7.5A2.25 2.25 0 0 1 5.25 9h13.5A2.25 2.25 0 0 1 21 11.25v7.5" />
</svg>,
Utleie:
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth="1.5" stroke="currentColor" className="md:size-6 size-8">
<path strokeLinecap="round" strokeLinejoin="round" d="M15.75 5.25a3 3 0 0 1 3 3m3 0a6 6 0 0 1-7.029 5.912c-.563-.097-1.159.026-1.563.43L10.5 17.25H8.25v2.25H6v2.25H2.25v-2.818c0-.597.237-1.17.659-1.591l6.499-6.499c.404-.404.527-1 .43-1.563A6 6 0 1 1 21.75 8.25Z" />
</svg>,
Medlem:
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth="1.5" stroke="currentColor" className="size-5">
<path strokeLinecap="round" strokeLinejoin="round" d="M18 7.5v3m0 0v3m0-3h3m-3 0h-3m-2.25-4.125a3.375 3.375 0 1 1-6.75 0 3.375 3.375 0 0 1 6.75 0ZM3 19.235v-.11a6.375 6.375 0 0 1 12.75 0v.109A12.318 12.318 0 0 1 9.374 21c-2.331 0-4.512-.645-6.374-1.766Z" />
</svg>,
Hjem:
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth="1.5" stroke="currentColor" className="size-5">
<path strokeLinecap="round" strokeLinejoin="round" d="m2.25 12 8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" />
</svg>,
ArrowRight_sm:
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth="1.5" stroke="currentColor" className="size-4">
<path strokeLinecap="round" strokeLinejoin="round" d="M13.5 4.5 21 12m0 0-7.5 7.5M21 12H3" />
</svg>,
ArrowLeft_sm:
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth="1.5" stroke="currentColor" className="size-4">
<path strokeLinecap="round" strokeLinejoin="round" d="M10.5 19.5 3 12m0 0 7.5-7.5M3 12h18" />
</svg>,
ArrowUp_sm:
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth="1.5" stroke="currentColor" className="size-4">
<path strokeLinecap="round" strokeLinejoin="round" d="m4.5 15.75 7.5-7.5 7.5 7.5" />
</svg>,
Facebook:
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" className='size-10' viewBox="0 0 48 48">
<path fill="#039be5" d="M24 5A19 19 0 1 0 24 43A19 19 0 1 0 24 5Z"></path><path fill="#fff" d="M26.572,29.036h4.917l0.772-4.995h-5.69v-2.73c0-2.075,0.678-3.915,2.619-3.915h3.119v-4.359c-0.548-0.074-1.707-0.236-3.897-0.236c-4.573,0-7.254,2.415-7.254,7.917v3.323h-4.701v4.995h4.701v13.729C22.089,42.905,23.032,43,24,43c0.875,0,1.729-0.08,2.572-0.194V29.036z"></path>
</svg>,
LinkedIn:
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" className='size-11' viewBox="0 0 48 48">
<path fill="#0288D1" d="M42,37c0,2.762-2.238,5-5,5H11c-2.761,0-5-2.238-5-5V11c0-2.762,2.239-5,5-5h26c2.762,0,5,2.238,5,5V37z"></path><path fill="#FFF" d="M12 19H17V36H12zM14.485 17h-.028C12.965 17 12 15.888 12 14.499 12 13.08 12.995 12 14.514 12c1.521 0 2.458 1.08 2.486 2.499C17 15.887 16.035 17 14.485 17zM36 36h-5v-9.099c0-2.198-1.225-3.698-3.192-3.698-1.501 0-2.313 1.012-2.707 1.99C24.957 25.543 25 26.511 25 27v9h-5V19h5v2.616C25.721 20.5 26.85 19 29.738 19c3.578 0 6.261 2.25 6.261 7.274L36 36 36 36z"></path>
</svg>,
ArrowDown:
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth="1.5" stroke="currentColor" className="size-6">
<path strokeLinecap="round" strokeLinejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" />
</svg>,
Pin:
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth="1.5" stroke="currentColor" className="size-5">
<path strokeLinecap="round" strokeLinejoin="round" d="M15 10.5a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
<path strokeLinecap="round" strokeLinejoin="round" d="M19.5 10.5c0 7.142-7.5 11.25-7.5 11.25S4.5 17.642 4.5 10.5a7.5 7.5 0 1 1 15 0Z" />
</svg>
};

return (
<div className="flex items-center justify-center">
{iconPaths[name]}
</div>
);
};

export default Icons;
119 changes: 119 additions & 0 deletions frontend/src/app/_components/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
'use client';

import shortcuts from '../data/shortcuts';
import { useEffect, useRef, useState } from 'react';
import ReactDOM from 'react-dom';
import Dropdown from './Dropdown';
import BreakLine from './Breakline';
import Icons from './Icons';
import Link from 'next/link';

const Navbar = () => {
const [openedNavbarSection, setOpenedNavbarSection] = useState<number>(-1);
const navbarRef = useRef<HTMLDivElement>(null); // Ref for the entire navbar

const handleClick = (index: number) => {
if (openedNavbarSection === index) {
setOpenedNavbarSection(-1); // Close if the same section is clicked again
} else {
setOpenedNavbarSection(index); // Open the clicked section
}
};

useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
// Check if the click is outside the navbar
if (navbarRef.current && !navbarRef.current.contains(event.target as Node)) {
setOpenedNavbarSection(-1); // Close the opened section
}
};

document.addEventListener('click', handleClickOutside);

// Lock scrolling if a dropdown is open
if (openedNavbarSection !== -1) {
document.body.style.overflow = 'hidden';
} else {
document.body.style.overflow = '';
}

return () => {
document.removeEventListener('click', handleClickOutside);
document.body.style.overflow = ''; // Ensure overflow is reset on cleanup
};
}, [openedNavbarSection]); // Add openedNavbarSection as a dependency

return (
<nav ref={navbarRef} className="py-2 fixed sticky top-0 bottom-auto z-50 font-semibold w-full bg-white shadow-xl">
{/* Backdrop when a dropdown is open */}
{openedNavbarSection !== -1 && ReactDOM.createPortal(
<div className="fixed inset-0 bg-black bg-opacity-25 z-10" />,
document.body
)}
<div className="flex justify-between items-center w-full">
<div className="flex items-center pl-4 mb-1 lg:pl-10">
<Link href={"/"}>
<img
src="/Kartai-logo_white.jpg" // Update with the correct path to your logo if different
alt="KartAI Logo"
className="h-10 pr-2" // Adjust height, width, and margin as needed
/>
</Link>
</div>
<div className="hidden sm:flex justify-end items-center pr-4 lg:pr-10">
{shortcuts.map((shortcut, index) => {
const isActive = index === openedNavbarSection;
return (
<div key={index} className="relative">
<button
onClick={() => handleClick(index)}
className={`relative group px-2 md:px-8 lg:px-5 py-2 text-md flex flex-row gap-2 cursor-hover items-center ${isActive ? 'text-kartAI-blue' : 'text-secondary-black'}`}
style={isActive ? { textDecoration: 'underline', textUnderlineOffset: '6px', textDecorationThickness: '2px' } : {}}
>
<p className="mt-1 whitespace-pre-wrap text-xs xl:text-sm hidden sm:block tracking-[0.15em]">
{shortcut.header.toUpperCase()}
</p>
</button>
{isActive && (
<div className="fixed left-0 right-0 pt-4 bg-white shadow-lg z-40 w-screen">
<BreakLine />
{/* Dynamically rendering subgroups and links for this section */}
<div className="w-screen-lg flex flex-col mt-4" >
<div className="px-10 md:px-20 pb-10 flex flex-wrap">
{shortcut.subgroups.map((subgroup, subgroupIndex) => (
<div key={subgroupIndex} className="w-full md:w-1/2 xl:w-1/3 p-2">
<h3 className="text-xl font-bold mb-2">{subgroup.title}</h3>
<div className={`flex flex-wrap ${subgroup.links.length > 3 ? '' : 'flex-col'}`}> {/* Adjust flex direction based on number of links */}
{subgroup.links.map((link, linkIndex) => (
<div key={linkIndex} className={`${subgroup.links.length > 3 ? 'w-1/2' : 'w-full'} p-1`}> {/* Conditional width based on number of links */}
<a
href={link.url}
className="block text-sm text-black hover:text-gray-600 py-1 flex flex-row gap-4"
>
{link.label}
{subgroup.arrow && <Icons name="ArrowRight_sm" />}
</a>
</div>
))}
</div>
</div>
))}
</div>
</div>
</div>
)}
</div>
);
})}
</div>
<div className="my-1 w-full sm:hidden flex items-center justify-between pl-4 relative text-white">
<p>
</p>
<Dropdown />
</div>
</div>
</nav>
);
};

export default Navbar;
50 changes: 0 additions & 50 deletions frontend/src/app/_components/post.tsx

This file was deleted.

Loading

0 comments on commit e885fb3

Please sign in to comment.