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
242 changes: 136 additions & 106 deletions frontend/src/components/Footer.tsx
Comment thread
Saurabh-2607 marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -26,99 +26,128 @@
setOpenSection((prev) => (prev === title ? null : title))
}, [])

const hasSocialSection = footerSections.some((section) =>
section.title.toLowerCase().startsWith('social')
)

const normalizedFooterSections = footerSections

return (
<footer className="mt-auto w-full border-t-1 border-t-slate-300 bg-slate-200 xl:max-w-full dark:border-t-slate-600 dark:bg-slate-800">
<div className="grid w-full place-content-center gap-6 px-4 py-4 text-slate-800 md:py-8 dark:text-slate-200">
<div className="grid w-full sm:grid-cols-2 sm:gap-x-10 sm:gap-y-6 md:grid-cols-4 md:gap-x-14 lg:gap-x-20">
{footerSections.map((section: Section) => (
<div key={section.title} className="flex flex-col gap-4">
{/*link*/}
<Button
disableAnimation
onPress={() => toggleSection(section.title)}
className="flex w-full items-center justify-between rounded-md bg-transparent pl-0 text-left text-lg font-semibold focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500 md:justify-start md:gap-2 lg:cursor-default"
aria-expanded={openSection === section.title}
aria-controls={`footer-section-${section.title}`}
>
<h3>{section.title}</h3>
{openSection === section.title ? (
<FaChevronUp className="h-4 w-4 transition-transform duration-200 lg:hidden" />
) : (
<FaChevronDown className="h-4 w-4 transition-transform duration-200 lg:hidden" />
)}
</Button>
<div
id={`footer-section-${section.title}`}
className={`flex flex-col gap-2 text-sm transition-all duration-300 ease-in-out ${
openSection === section.title
? 'max-h-96'
: 'max-h-0 overflow-hidden lg:max-h-full lg:overflow-visible'
}`}
>
{section.links.map((link) => {
const isExternal = link.href?.startsWith('http')
<div className="w-full px-10 py-4 text-slate-800 md:px-8 md:py-8 dark:text-slate-200">
<div className="mx-auto grid w-full max-w-6xl gap-6">
<div className="grid w-full sm:grid-cols-2 sm:gap-x-8 sm:gap-y-6 md:grid-cols-[repeat(5,max-content)] md:justify-between md:gap-x-8 lg:gap-x-8">
{normalizedFooterSections.map((section: Section) => (
<div key={section.title} className="flex flex-col gap-4 pl-2">
{/*link*/}
<Button
disableAnimation
onPress={() => toggleSection(section.title)}
className="flex w-full items-center justify-between rounded-md bg-transparent pl-0 text-left text-lg font-semibold focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500 md:justify-start md:gap-2 lg:cursor-default"
aria-expanded={openSection === section.title}
aria-controls={`footer-section-${section.title}`}
>
<h3>{section.title}</h3>
{openSection === section.title ? (
<FaChevronUp className="h-4 w-4 transition-transform duration-200 lg:hidden" />
) : (
<FaChevronDown className="h-4 w-4 transition-transform duration-200 lg:hidden" />
)}
</Button>
<div
id={`footer-section-${section.title}`}
className={`flex flex-col gap-2 text-sm transition-all duration-300 ease-in-out ${
openSection === section.title
? 'max-h-96'
: 'max-h-0 overflow-hidden lg:max-h-full lg:overflow-visible'
}`}
>
{section.links.map((link) => {
const isExternal = link.href?.startsWith('http')
const isSocialSection = section.title.toLowerCase().startsWith('social')
const social = isSocialSection
? footerIcons.find((icon) => icon.label === link.text)
: undefined
const SocialIcon = social?.icon

return (
<div key={link.href || `span-${link.text}`} className="py-1">
{link.isSpan ? (
<span className="text-slate-600 dark:text-slate-400">{link.text}</span>
) : (
<Link
className="rounded-md text-slate-600 hover:text-slate-900 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500 dark:text-slate-400 dark:hover:text-slate-100"
href={link.href || '/'}
target={isExternal ? '_blank' : undefined}
rel={isExternal ? 'noopener noreferrer' : undefined}
>
{link.text}
</Link>
)}
</div>
)
})}
return (
<div key={link.href || `span-${link.text}`} className="">
{link.isSpan ? (
<span className="text-slate-600 dark:text-slate-400">{link.text}</span>
) : isSocialSection ? (
<a
className="flex items-center gap-2 rounded-md py-0.5 text-slate-600 hover:text-slate-900 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500 dark:text-slate-400 dark:hover:text-slate-100"
href={link.href || '/'}
target={isExternal ? '_blank' : undefined}
rel={isExternal ? 'noopener noreferrer' : undefined}
aria-label={`OWASP Nest ${link.text}`}
>
{SocialIcon && <SocialIcon className="h-4 w-4" />}
<span>{link.text}</span>
</a>
) : (
<Link
className="flex items-center gap-2 rounded-md py-0.5 text-slate-600 hover:text-slate-900 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500 dark:text-slate-400 dark:hover:text-slate-100"
href={link.href || '/'}
target={isExternal ? '_blank' : undefined}
rel={isExternal ? 'noopener noreferrer' : undefined}
>
<span>{link.text}</span>
</Link>
)}

Check warning on line 97 in frontend/src/components/Footer.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=OWASP_Nest&issues=AZ096cUPAyPb1RzVPto9&open=AZ096cUPAyPb1RzVPto9&pullRequest=4334
</div>
)
})}
</div>
</div>
</div>
))}
</div>
))}
{!hasSocialSection && (
<div className="flex flex-col gap-4 pl-2">
<h3>Socials</h3>
<div className="flex flex-col gap-2 text-sm">
{footerIcons.map((social) => {
const SocialIcon = social.icon

{/* Social Media Icons Section */}
<div className="mb-0 flex flex-row justify-center gap-6">
{footerIcons.map((social) => {
const SocialIcon = social.icon
return (
<Link
key={social.label}
href={social.href}
target="_blank"
rel="noopener noreferrer"
aria-label={`OWASP Nest ${social.label}`}
className="rounded-full p-2 text-slate-600 transition-colors duration-200 hover:text-slate-900 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500 dark:text-slate-400 dark:hover:text-slate-100"
>
<SocialIcon className="h-4 w-4" />
</Link>
)
})}
</div>
return (
<div key={social.label} className="">
<a
className="flex items-center gap-2 rounded-md py-0.5 text-slate-600 hover:text-slate-900 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500 dark:text-slate-400 dark:hover:text-slate-100"
href={social.href}
target="_blank"
rel="noopener noreferrer"
aria-label={`OWASP Nest ${social.label}`}
>
<SocialIcon className="h-4 w-4" />
{social.label}
</a>
</div>
)
})}
</div>
</div>
)}
</div>

{/* Logos Section */}
<div className="flex items-center justify-center gap-6">
<Link
href="https://owasp.org/"
target="_blank"
rel="noopener noreferrer"
className="rounded focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500"
aria-label="OWASP Foundation"
>
<Image
src="/img/OWASP_logo.svg"
alt="OWASP Logo"
width={100}
height={32}
className="h-8 w-auto dark:invert"
/>
</Link>
{/* Logos Section */}
<div className="flex items-center justify-center gap-6">
<Link
href="https://owasp.org/"
target="_blank"
rel="noopener noreferrer"
className="rounded focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500"
aria-label="OWASP Foundation"
>
<Image
src="/img/OWASP_logo.svg"
alt="OWASP Logo"
width={100}
height={32}
className="h-8 w-auto dark:invert"
/>
</Link>

{/* Vertical Separator */}
<div className="h-8 w-px bg-slate-400 dark:bg-white"></div>
{/* Vertical Separator */}
<div className="h-8 w-px bg-slate-400 dark:bg-white"></div>

<Link
href="/"
Expand All @@ -137,28 +166,29 @@
</Link>
</div>

{/* Footer bottom section with copyright and version */}
<div className="grid w-full place-content-center">
<div className="flex w-full flex-col items-center gap-2 sm:flex-col sm:text-left">
<p className="text-sm text-slate-600 dark:text-slate-400">
© <span id="year">{new Date().getFullYear()}</span> OWASP Nest. All rights reserved.
</p>
{RELEASE_VERSION && (
{/* Footer bottom section with copyright and version */}
<div className="grid w-full place-content-center">
<div className="flex w-full flex-col items-center gap-2 sm:flex-col sm:text-left">
<p className="text-sm text-slate-600 dark:text-slate-400">
<Link
className="rounded-md text-slate-600 hover:text-slate-900 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500 dark:text-slate-400 dark:hover:text-slate-100"
href={
ENVIRONMENT === 'production'
? `https://github.com/OWASP/Nest/releases/tag/${RELEASE_VERSION}`
: `https://github.com/OWASP/Nest/commit/${RELEASE_VERSION.split('-').pop()}`
}
rel="noopener noreferrer"
target="_blank"
>
<span>v{RELEASE_VERSION}</span>
</Link>
© <span id="year">{new Date().getFullYear()}</span> OWASP Nest. All rights reserved.
</p>
)}
{RELEASE_VERSION && (
<p className="text-sm text-slate-600 dark:text-slate-400">
<Link
className="rounded-md text-slate-600 hover:text-slate-900 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500 dark:text-slate-400 dark:hover:text-slate-100"
href={
ENVIRONMENT === 'production'
? `https://github.com/OWASP/Nest/releases/tag/${RELEASE_VERSION}`
: `https://github.com/OWASP/Nest/commit/${RELEASE_VERSION.split('-').pop()}`
}
rel="noopener noreferrer"
target="_blank"
>
<span>v{RELEASE_VERSION}</span>
</Link>
</p>
)}
</div>
</div>
</div>
</div>
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ export const footerSections: Section[] = [
{ text: 'Team', href: 'https://owasp.org/corporate/' },
],
},
{
title: 'Socials',
links: footerIcons.map((social) => ({ href: social.href, text: social.label })),
},
]

export const tooltipStyle = {
Expand Down