Skip to content
Merged
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
272 changes: 123 additions & 149 deletions app/components/Footer/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,198 +12,172 @@ import {
useBreakpointValue,
Container,
Flex,
Tooltip,
Divider,
} from "@chakra-ui/react";

import { HelpUsButton } from "./HelpUsButton";

// Constants for better maintainability
const FOOTER_CONFIG = {
hebrewText: "הופכים עליהם, טובים אותם",
reverseImageUrl:
"https://twitter.com/kann/status/1712897481837539810?t=kxXrXgX59tp1yPnrYiS4Iw&s=19",
reverseImageAlt: "Reverse Initiative",
reverseImageSrc: "/images/reverse.png",
} as const;

const FOOTER_STYLES = {
container: {
borderTopWidth: "1px",
borderColor: "gray.200",
backgroundColor: "white",
position: "sticky" as const,
bottom: 0,
zIndex: 999,
boxShadow: "0 -2px 10px rgba(0, 0, 0, 0.1)",
},
content: {
fontFamily: "Roboto, sans-serif",
py: { base: 3, md: 4 },
px: { base: 4, md: 6 },
},
hebrewText: {
textAlign: "center" as const,
fontSize: { base: "12px", md: "14px" },
color: "gray.600",
fontWeight: "medium",
},
imageBox: {
boxSize: { base: "28px", md: "36px" },
borderRadius: "md",
overflow: "hidden",
transition: "transform 0.2s ease",
_hover: {
transform: "scale(1.05)",
},
},
socialButton: {
size: { base: "sm", md: "md" },
variant: "ghost",
borderRadius: "full",
transition: "all 0.2s ease",
_hover: {
transform: "translateY(-2px)",
boxShadow: "md",
},
_focus: {
boxShadow: "outline",
},
},
copyrightText: "Links For Israel",
} as const;

export function Footer() {
// Responsive values
const isMobile = useBreakpointValue({ base: true, md: false });
const socialIconSize = useBreakpointValue({ base: "20px", md: "24px" });
const showHelpButton = useBreakpointValue({ base: false, md: true });
const socialIconSize = useBreakpointValue({ base: "18px", md: "20px" });

Comment on lines 30 to 32

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Footer missing use client 🐞 Bug ✓ Correctness

Footer calls useBreakpointValue (a React hook) but the module is not marked with `"use
  client"`, which is required if it’s imported by a Server Component.
• app/layout.tsx (server by default) imports Footer; this can trigger Next.js build/runtime
  errors about using client-only hooks in Server Components / missing client boundary.
• This is high impact because it can prevent the app from building or rendering correctly depending
  on Next’s module graph/tree-shaking.
Agent Prompt
### Issue description
`Footer` uses Chakra hooks (`useBreakpointValue`) but the module is not explicitly marked as a client component. In the Next.js app router, this becomes a problem when a Server Component imports it (currently `app/layout.tsx` imports `Footer`).

### Issue Context
This can lead to Next.js build/runtime errors around client-only hooks being used from Server Components, depending on module graph evaluation.

### Fix Focus Areas
- app/components/Footer/Footer.tsx[1-33]
- app/layout.tsx[1-10]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

// Mobile layout - stack vertically
if (isMobile) {
return (
<Box as="footer" {...FOOTER_STYLES.container}>
<Container maxW="container.xl" {...FOOTER_STYLES.content}>
<VStack spacing={3} align="center">
{/* Hebrew text and reverse image */}
<VStack spacing={2} align="center">
<Text {...FOOTER_STYLES.hebrewText}>
return (
<Box
as="footer"
position="sticky"
bottom={0}
zIndex={999}
bg="linear-gradient(135deg, #1a1a2e 0%, #16213e 100%)"
borderTop="1px solid"
borderColor="whiteAlpha.100"
backdropFilter="blur(10px)"
>
<Container
maxW="container.xl"
py={{ base: 4, md: 5 }}
px={{ base: 4, md: 8 }}
>
{isMobile ? (
// Mobile Layout
<VStack spacing={4}>
{/* Hebrew motto with subtle styling */}
<HStack spacing={3} align="center">
<Link
href={FOOTER_CONFIG.reverseImageUrl}
isExternal
aria-label="קישור לפוסט המקורי"
_hover={{ transform: "scale(1.1)" }}
transition="transform 0.2s ease"
>
<Image
src={FOOTER_CONFIG.reverseImageSrc}
alt={FOOTER_CONFIG.reverseImageAlt}
boxSize="28px"
borderRadius="md"
/>
</Link>
<Text
fontSize="sm"
fontWeight="500"
color="whiteAlpha.900"
letterSpacing="wide"
>
{FOOTER_CONFIG.hebrewText}
</Text>
<Tooltip label="לפוסט המקורי בטוויטר" placement="top">
<Link
href={FOOTER_CONFIG.reverseImageUrl}
</HStack>

{/* Social links */}
<HStack spacing={1}>
{socialLinks.map((link) => (
<IconButton
key={link.href}
as={Link}
href={link.href}
isExternal
aria-label="קישור לפוסט המקורי בטוויטר"
>
<Box {...FOOTER_STYLES.imageBox}>
aria-label={link.alt}
icon={
<Image
src={FOOTER_CONFIG.reverseImageSrc}
alt={FOOTER_CONFIG.reverseImageAlt}
width={FOOTER_STYLES.imageBox.boxSize}
height={FOOTER_STYLES.imageBox.boxSize}
loading="lazy"
src={link.imgSrc}
alt={link.alt}
boxSize={socialIconSize}
filter="brightness(0) invert(1)"
opacity={0.8}
/>
</Box>
</Link>
</Tooltip>
</VStack>

{/* Social media links */}
<HStack spacing={1} justify="center" wrap="wrap">
{socialLinks.map((link, index) => (
<Tooltip key={link.href} label={link.alt} placement="top">
<IconButton
as={Link}
href={link.href}
isExternal
aria-label={`עבור ל${link.alt}`}
icon={
<Image
src={link.imgSrc}
alt={link.alt}
boxSize={socialIconSize}
loading="lazy"
/>
}
{...FOOTER_STYLES.socialButton}
/>
</Tooltip>
}
variant="ghost"
size="sm"
borderRadius="full"
color="whiteAlpha.800"
_hover={{
bg: "whiteAlpha.200",
transform: "translateY(-2px)",
}}
transition="all 0.2s ease"
/>
))}
</HStack>

{/* Help button for mobile */}
<Box mt={2}>
<HelpUsButton />
</Box>
{/* Dev button */}
<HelpUsButton />
</VStack>
</Container>
</Box>
);
}

// Desktop layout - horizontal
return (
<Box as="footer" {...FOOTER_STYLES.container}>
<Container maxW="container.xl" {...FOOTER_STYLES.content}>
<Flex
justify="space-between"
align="center"
direction="row"
wrap="nowrap"
>
{/* Left section: Hebrew text and reverse image */}
<VStack spacing={2} align="center" flex="0 0 auto">
<Text {...FOOTER_STYLES.hebrewText}>
{FOOTER_CONFIG.hebrewText}
</Text>
<Tooltip label="לפוסט המקורי בטוויטר" placement="top">
) : (
// Desktop Layout
<Flex justify="space-between" align="center">
{/* Left: Motto and reverse image */}
<HStack spacing={4}>
<Link
href={FOOTER_CONFIG.reverseImageUrl}
isExternal
aria-label="קישור לפוסט המקורי בטוויטר"
aria-label="קישור לפוסט המקורי"
_hover={{ transform: "scale(1.1)" }}
transition="transform 0.2s ease"
>
<Box {...FOOTER_STYLES.imageBox}>
<Image
src={FOOTER_CONFIG.reverseImageSrc}
alt={FOOTER_CONFIG.reverseImageAlt}
width={FOOTER_STYLES.imageBox.boxSize}
height={FOOTER_STYLES.imageBox.boxSize}
loading="lazy"
/>
</Box>
<Image
src={FOOTER_CONFIG.reverseImageSrc}
alt={FOOTER_CONFIG.reverseImageAlt}
boxSize="32px"
borderRadius="md"
/>
</Link>
</Tooltip>
</VStack>
<Divider
orientation="vertical"
h="24px"
borderColor="whiteAlpha.300"
/>
<Text
fontSize="sm"
fontWeight="500"
color="whiteAlpha.900"
letterSpacing="wide"
>
{FOOTER_CONFIG.hebrewText}
</Text>
</HStack>

{/* Center section: Help button */}
{showHelpButton && (
<Box flex="0 0 auto">
<HelpUsButton />
</Box>
)}
{/* Center: Dev button */}
<HelpUsButton />

{/* Right section: Social media links */}
<HStack spacing={2} align="center" flex="0 0 auto">
{socialLinks.map((link, index) => (
<Tooltip key={link.href} label={link.alt} placement="top">
{/* Right: Social links */}
<HStack spacing={1}>
{socialLinks.map((link) => (
<IconButton
key={link.href}
as={Link}
href={link.href}
isExternal
aria-label={`עבור ל${link.alt}`}
aria-label={link.alt}
icon={
<Image
src={link.imgSrc}
alt={link.alt}
boxSize={socialIconSize}
loading="lazy"
filter="brightness(0) invert(1)"
opacity={0.85}
/>
}
{...FOOTER_STYLES.socialButton}
variant="ghost"
size="md"
borderRadius="full"
color="whiteAlpha.800"
_hover={{
bg: "whiteAlpha.200",
transform: "translateY(-2px)",
}}
transition="all 0.2s ease"
/>
</Tooltip>
))}
</HStack>
</Flex>
))}
</HStack>
</Flex>
)}
</Container>
</Box>
);
Expand Down
34 changes: 18 additions & 16 deletions app/components/Footer/HelpUsButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,38 @@ import React from "react";

import { Link } from "@chakra-ui/next-js";

const displayText = "< כניסה למפתחים >";

export const HelpUsButton = (): JSX.Element => {
return (
<Link
href="https://github.com/4tals/LinksForIsrael/blob/main/docs/contribute.md"
isExternal
sx={{
fontFamily: "'Consolas', 'Monaco', 'Source Code Pro', monospace",
backgroundColor: "blue.500", // Standard blue color
color: "white",
padding: { base: "8px 18px", md: "6px 12px" },
fontSize: { base: "xs", md: "sm" }, //bas Smaller font size on mobile (xs) and slightly larger on tablet and above (sm)
border: "none",
borderRadius: "8px",
fontFamily: "'JetBrains Mono', 'Fira Code', 'Consolas', monospace",
fontSize: { base: "xs", md: "sm" },
fontWeight: "500",
letterSpacing: "0.5px",
color: "whiteAlpha.900",
bg: "transparent",
border: "1px solid",
borderColor: "whiteAlpha.300",
borderRadius: "full",
px: { base: 4, md: 5 },
py: { base: 2, md: 2 },
textDecoration: "none",
boxShadow: "0px 4px 12px rgba(0, 0, 0, 0.1)",
transition: "transform 0.3s ease, background-color 0.3s ease",
transition: "all 0.3s ease",
_hover: {
backgroundColor: "blue.600",
transform: "translateY(-2px)",
boxShadow: "0px 6px 16px rgba(0, 0, 0, 0.2)",
bg: "whiteAlpha.100",
borderColor: "whiteAlpha.500",
transform: "translateY(-1px)",
textDecoration: "none",
},
_focus: {
outline: "none",
boxShadow: "0 0 0 3px rgba(66, 153, 225, 0.6)",
boxShadow: "0 0 0 2px rgba(255, 255, 255, 0.2)",
},
}}
>
{displayText}
{"<"} Contribute {"/>"}
</Link>
);
};