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
21 changes: 13 additions & 8 deletions app/changelog/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { GitHubIcon } from '@/components/icons'
import { Badge, Heading, Text } from "@/components/ui";
import { CHANGELOG_ENTRIES } from "@/lib/changelog-data";
import { CHANGELOG_ENTRIES, type ChangelogEntry } from "@/lib/changelog-data";
import { Metadata } from "next";
import { Header } from "@/components/header";
import { FooterSmall } from "@/components/footer-small";
Expand All @@ -16,8 +16,8 @@ export const metadata: Metadata = {
};

export default function ChangelogPage() {

const sortedEntries = [...CHANGELOG_ENTRIES].sort((a, b) => b.timeStamp - a.timeStamp);
const sortedEntries: ChangelogEntry[] = [...CHANGELOG_ENTRIES]
.sort((a, b) => b.timeStamp - a.timeStamp);

// Helper to parse [Text](URL) into Teal Links
const formatChangelogText = (text: string) => {
Expand Down Expand Up @@ -65,7 +65,12 @@ export default function ChangelogPage() {
{/* The Timeline Line */}
<div className="absolute left-[11px] md:left-[155px] top-2 bottom-0 w-px bg-border hidden sm:block" />

{sortedEntries.map((entry) => (
{sortedEntries.length === 0 ? (
<div className="text-center py-12">
<Text className="text-muted-foreground">Changelog entries will appear here soon.</Text>
</div>
) : (
sortedEntries.map((entry) => (
<article key={entry.timeStamp} className="relative grid grid-cols-1 md:grid-cols-[140px_1fr] gap-8 md:gap-12 pb-16 border-b border-border last:border-0">

{/* Left Column: Date & Version */}
Expand All @@ -77,7 +82,7 @@ export default function ChangelogPage() {
</Badge>
</div>
<div className="hidden md:flex flex-col items-end gap-2 mt-4">
{entry.prLinks.map((pr, i) => (
{entry.prLinks.map((pr: { link: string; number: string }, i: number) => (
<a
key={i}
href={pr.link}
Expand All @@ -100,7 +105,7 @@ export default function ChangelogPage() {
</div>

<ul className="space-y-3">
{entry.changes.map((change, i) => (
{entry.changes.map((change: { text: string }, i: number) => (
<li key={i} className="flex items-start gap-3">
<div className="mt-2 w-1.5 h-1.5 rounded-full shrink-0 bg-foreground" />
<span className="text-[15px] text-foreground/90">{formatChangelogText(change.text)}</span>
Expand All @@ -110,7 +115,7 @@ export default function ChangelogPage() {

{/* Mobile PR Link */}
<div className="flex md:hidden flex-wrap gap-x-4 gap-y-2 pt-4 border-t border-border">
{entry.prLinks.map((pr, i) => (
{entry.prLinks.map((pr: { link: string; number: string }, i: number) => (
<a
key={i}
href={pr.link}
Expand All @@ -123,7 +128,7 @@ export default function ChangelogPage() {
</div>
</div>
</article>
))}
)))}
</div>

</div>
Expand Down
22 changes: 16 additions & 6 deletions components/Footer.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
"use client";

Comment on lines +1 to +2
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Unnecessary "use client" conversion — also affects faq.tsx, value-blocks.tsx, latest-articles.tsx

Footer, FaqComponent, all four *Block functions in value-blocks.tsx, and LatestArticles are purely display components whose entire content is static. Adding "use client" on each forces the full component tree — static content, constants, markup — into the client JS bundle on every page that imports them, increasing bundle size unnecessarily.

The preferred pattern in the App Router is to keep the parent as a Server Component and extract only the animated wrapper into a thin "use client" boundary:

// animated-section.client.tsx  ← thin CC wrapper
"use client";
import { motion } from "framer-motion";
import { staggerContainer, staggerItem, defaultViewport } from "@/lib/animations";

export function AnimatedSection({ children }: { children: React.ReactNode }) {
  return (
    <motion.div
      variants={staggerContainer}
      initial="hidden"
      whileInView="visible"
      viewport={defaultViewport}
    >
      {children}
    </motion.div>
  );
}

Then the Server Component imports the wrapper and passes static children into it, preserving server rendering for everything except the animation shell.

As per coding guidelines, follow existing project design patterns strictly — converting static Server Components to Client Components diverges from the App Router's SC/CC split that the rest of the codebase maintains.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/Footer.tsx` around lines 1 - 2, These components (Footer,
FaqComponent, LatestArticles, and the four functions in value-blocks.tsx) are
static and should not be "use client"; remove the "use client" directive from
each and keep them as Server Components, then create a thin client-side animated
wrapper (e.g., AnimatedSection or AnimatedWrapper in a .client.tsx file that
uses motion/framer-motion and the project's stagger variants) and import that
wrapper into the server components to wrap only the animated markup; update the
server components to pass their static children into that client wrapper so only
the animation shell is bundled to the client.

import Link from "next/link";
import { motion } from "framer-motion";
import { Section, Heading, Text } from "@/components/ui";
import { SocialLinks } from "@/components/social-links";
import { FOOTER_NAVIGATION_ITEMS, FOOTER_COPYRIGHT } from "@/components/footer-common";
import { fadeInUp, staggerContainer, staggerItem, defaultViewport } from "@/lib/animations";

export const Footer = () => {

return (
<Section className="bg-foreground text-background dark:bg-card dark:text-white">
<div className="grid lg:grid-cols-2 gap-18 items-center">
<div className="flex gap-8 flex-col items-start">
<motion.div
variants={staggerContainer}
initial="hidden"
whileInView="visible"
viewport={defaultViewport}
className="grid lg:grid-cols-2 gap-18 items-center"
>
<motion.div variants={staggerItem} className="flex gap-8 flex-col items-start">
<div className="flex gap-2 flex-col">
<Heading>GSoC Organizations Guide</Heading>
<Text className="max-w-lg text-background/75 dark:text-white">
Expand All @@ -27,8 +37,8 @@ export const Footer = () => {
className="flex items-center gap-4 text-black"
textColor="white"
/>
</div>
<div className="grid lg:grid-cols-3 gap-10 items-start">
</motion.div>
<motion.div variants={staggerItem} className="grid lg:grid-cols-3 gap-10 items-start">
{FOOTER_NAVIGATION_ITEMS.map((item) => (
<div
key={item.title}
Expand All @@ -51,8 +61,8 @@ export const Footer = () => {
</div>
</div>
))}
</div>
</div>
</motion.div>
</motion.div>
</Section>
);
};
23 changes: 20 additions & 3 deletions components/faq.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
"use client";

import { motion } from "framer-motion";
import {
Accordion,
AccordionContent,
Expand All @@ -6,6 +9,7 @@ import {
Section,
SectionHeader,
} from "@/components/ui";
import { fadeInLeft, fadeInRight, defaultViewport } from "@/lib/animations";

export const gsocFaq = [
{
Expand Down Expand Up @@ -64,21 +68,34 @@ export const gsocFaq = [
export const FaqComponent = () => (
<Section>
<div className="grid lg:grid-cols-2 gap-10">
<div className="flex gap-10 flex-col">
<motion.div
variants={fadeInLeft}
initial="hidden"
whileInView="visible"
viewport={defaultViewport}
className="flex gap-10 flex-col"
>
<SectionHeader
badge="FAQ"
title="Google Summer of Code Related Queries"
description="Understanding GSoC organizations, project ideas, timelines, and proposal requirements can be challenging. This FAQ gives you clear, concise answers to the most common GSoC queries helping you prepare smarter and avoid confusion."
/>
</div>
<Accordion type="single" collapsible className="w-full">
</motion.div>
<motion.div
variants={fadeInRight}
initial="hidden"
whileInView="visible"
viewport={defaultViewport}
>
<Accordion type="single" collapsible className="w-full">
{gsocFaq.map((faq, index) => (
<AccordionItem key={index} value={"index-" + index}>
<AccordionTrigger>{faq.question}</AccordionTrigger>
<AccordionContent>{faq.answer}</AccordionContent>
</AccordionItem>
))}
</Accordion>
</motion.div>
</div>
</Section>
);
9 changes: 7 additions & 2 deletions components/header.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
'use client'
import Link from 'next/link'
import { motion } from 'framer-motion'
import { Menu, X } from 'lucide-react'
import { Button } from "@/components/ui"
import React from 'react'
import { cn } from '@/lib/utils'
import { SOCIAL_LINKS } from '@/components/footer-common'
import { GitHubIcon, XIcon } from '@/components/icons'
import { ModeToggle } from './ModeToggle'
import { fadeInUp } from '@/lib/animations'

const menuItems = [
{ name: 'Organizations', href: '/organizations' },
Expand All @@ -32,7 +34,10 @@ export const Header = () => {
}, [])
return (
<header suppressHydrationWarning>
<nav
<motion.nav
initial={{ y: -100, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{ duration: 0.5, ease: "easeOut" }}
data-state={menuState && 'active'}
className="fixed z-20 w-full px-2"
suppressHydrationWarning>
Expand Down Expand Up @@ -156,7 +161,7 @@ export const Header = () => {
</div>
</div>
</div>
</nav>
</motion.nav>
</header>
)
}
22 changes: 14 additions & 8 deletions components/hero-component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { motion } from "framer-motion";
import { MoveRight } from "lucide-react";
import Link from "next/link";
import { Button, Section, Heading, Text } from "@/components/ui";
import { staggerContainer, staggerItem } from "@/lib/animations";

export const HeroComponent = () => {
const [titleNumber, setTitleNumber] = useState(0);
Expand All @@ -26,13 +27,18 @@ export const HeroComponent = () => {

return (
<Section>
<div className="flex gap-8 items-center justify-center flex-col mt-20">
<div>
<motion.div
variants={staggerContainer}
initial="hidden"
animate="visible"
className="flex gap-8 items-center justify-center flex-col mt-20"
>
<motion.div variants={staggerItem}>
<Button variant="secondary" size="sm" className="gap-4">
Read our launch article <MoveRight className="w-4 h-4" />
</Button>
</div>
<div className="flex gap-4 flex-col">
</motion.div>
<motion.div variants={staggerItem} className="flex gap-4 flex-col">
<Heading as="h1" variant="hero" className="max-w-2xl text-center">
<span className="text-spektr-cyan-50">Making GSOC</span>
<span className="relative flex w-full justify-center overflow-hidden text-center md:pb-4 md:pt-1">
Expand Down Expand Up @@ -64,15 +70,15 @@ export const HeroComponent = () => {
<Text variant="lead" className="max-w-2xl text-center">
Find the right GSoC organizations, understand what they expect, explore project ideas, study previous GSoC projects, and prepare smarter with curated resources, timelines, and guides all in one place.
</Text>
</div>
<div className="flex flex-row gap-3">
</motion.div>
<motion.div variants={staggerItem} className="flex flex-row gap-3">
<Button asChild size="lg" className="gap-4">
<Link href="/organizations" prefetch={true}>
View All GSoC Orgs <MoveRight className="w-4 h-4" />
</Link>
</Button>
</div>
</div>
</motion.div>
</motion.div>
</Section>
);
};
35 changes: 26 additions & 9 deletions components/latest-articles.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
"use client";

import { motion } from "framer-motion";
import { MoveRight } from "lucide-react";
import Link from "next/link";
import Image from "next/image";
import { Button } from "@/components/ui/button";
import { fadeInUp, staggerContainer, staggerItem, defaultViewport } from "@/lib/animations";

interface Article {
id: string;
Expand Down Expand Up @@ -50,7 +54,13 @@ export function LatestArticles() {
<section className="w-full py-12 lg:py-20">
<div className="max-w-6xl mx-auto px-6 lg:px-12">
<div className="flex flex-col gap-14">
<div className="flex w-full flex-col sm:flex-row sm:justify-between sm:items-center gap-8">
<motion.div
variants={fadeInUp}
initial="hidden"
whileInView="visible"
viewport={defaultViewport}
className="flex w-full flex-col sm:flex-row sm:justify-between sm:items-center gap-8"
>
<h2 className="text-3xl md:text-5xl tracking-tighter max-w-xl font-regular">
Latest articles
</h2>
Expand All @@ -59,14 +69,20 @@ export function LatestArticles() {
View all articles <MoveRight className="w-4 h-4" />
</Link>
</Button>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-8">
</motion.div>
<motion.div
variants={staggerContainer}
initial="hidden"
whileInView="visible"
viewport={defaultViewport}
className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-8"
>
{COMING_SOON_ARTICLES.map((article) => (
<Link
key={article.id}
href="/blog"
className="flex flex-col gap-2 hover:opacity-75 cursor-pointer transition-opacity"
>
<motion.div key={article.id} variants={staggerItem}>
<Link
href="/blog"
className="flex flex-col gap-2 hover:opacity-75 cursor-pointer transition-opacity"
>
<div className="bg-muted rounded-md aspect-video mb-4 overflow-hidden">
<Image
src={ARTICLE_IMAGES[article.id]}
Expand All @@ -82,8 +98,9 @@ export function LatestArticles() {
{article.description}
</p>
</Link>
</motion.div>
))}
</div>
</motion.div>
</div>
</div>
</section>
Expand Down
30 changes: 21 additions & 9 deletions components/organization-card.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
"use client";

import Link from "next/link";
import Image from "next/image";
import { motion } from "framer-motion";
import { Organization } from "@/lib/api";
import { Badge } from "@/components/ui";
import { cn } from "@/lib/utils";
import { fadeInUp, defaultViewport } from "@/lib/animations";

interface OrganizationCardProps {
org: Organization;
Expand Down Expand Up @@ -39,15 +43,22 @@ export function OrganizationCard({
const effectiveShowTechStack = isCompact ? false : showTechStack;

return (
<Link
href={`/organizations/${org.slug}`}
prefetch={true}
className={cn(
"block bg-white border border-gray-200 rounded-xl p-5 hover:shadow-md hover:border-gray-300 transition-all w-full",
"dark:bg-card dark:border-border dark:hover:border-gray-600",
className
)}
<motion.div
variants={fadeInUp}
initial="hidden"
whileInView="visible"
viewport={defaultViewport}
className="h-full"
>
<Link
href={`/organizations/${org.slug}`}
prefetch={true}
className={cn(
"block bg-white border border-gray-200 rounded-xl p-5 hover:shadow-md hover:border-gray-300 transition-all w-full h-full",
"dark:bg-card dark:border-border dark:hover:border-gray-600",
className
)}
>
{/* Header with Logo */}
<div className="flex items-start gap-4 mb-3">
<div className="w-12 h-12 rounded-lg bg-gray-100 dark:bg-muted flex items-center justify-center overflow-hidden shrink-0">
Expand Down Expand Up @@ -119,6 +130,7 @@ export function OrganizationCard({
</div>
</div>
)}
</Link>
</Link>
</motion.div>
);
}
Loading