Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
Binary file added projects-screenshot.png
Copy link
Collaborator

Choose a reason for hiding this comment

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

Remove this file.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
124 changes: 75 additions & 49 deletions src/app/projects/page.jsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
'use client'

import Link from 'next/link';
import Grid from '@mui/material/Grid';
import MuiCard from '@mui/material/Card';
import CardContent from '@mui/material/CardContent';
import CardActions from '@mui/material/CardActions';
import Typography from '@mui/material/Typography';
import Button from '@mui/material/Button';
import ArrowForwardIcon from '@mui/icons-material/ArrowForward';
import { Container } from '@/components/shared/Container';
import { Banner } from '@/components/shared/Banner';
import Image from 'next/image';
import Link from 'next/link'
import Grid from '@mui/material/Grid'
import MuiCard from '@mui/material/Card'
import CardContent from '@mui/material/CardContent'
import CardActions from '@mui/material/CardActions'
import Typography from '@mui/material/Typography'
import Button from '@mui/material/Button'
import ArrowForwardIcon from '@mui/icons-material/ArrowForward'
import { Container } from '@/components/shared/Container'
import { Banner } from '@/components/shared/Banner'
import Image from 'next/image'
import projects from '@/helper/projects'
import { CardProduct } from '@/components/products/CardProduct'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faDiscord, faGithub } from '@fortawesome/free-brands-svg-icons'
import { motion } from 'framer-motion'


function LinkIcon(props) {
return (
<svg viewBox="0 0 24 24" aria-hidden="true" {...props}>
Expand All @@ -32,16 +31,26 @@ function LinkIcon(props) {
// Define the Cards component here
const Cards = ({ projectList }) => {
return (
<Grid container spacing={{ xs: 2, md: 4 }} sx={{ paddingTop: '40px', justifyContent: 'center' }}>
<Grid
container
spacing={{ xs: 2, md: 4 }}
sx={{ paddingTop: '40px', justifyContent: 'center' }}
>
{projectList.map((project, index) => (
<Grid item xs={12} sm={6} md={4} key={index} component={motion.div}
<Grid
item
xs={12}
sm={6}
md={4}
key={index}
component={motion.div}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5, delay: index * 0.1 }}
>
<MuiCard
className='dark:bg-[#2A2A2A] dark:border-white transition-transform hover:scale-[1.02] duration-300'
className="transition-transform duration-300 hover:scale-[1.02] dark:border-white dark:bg-[#2A2A2A]"
sx={{
height: 400,
borderRadius: 2,
Expand Down Expand Up @@ -75,7 +84,7 @@ const Cards = ({ projectList }) => {

<Typography
variant="body1"
className="text-zinc-600 dark:text-zinc-400 text-lg font-mono leading-7 text-center"
className="text-center font-mono text-lg leading-7 text-zinc-600 dark:text-zinc-400"
sx={{
fontFamily: 'Nunito-Light',
color: 'black',
Expand All @@ -90,76 +99,94 @@ const Cards = ({ projectList }) => {
{project.description}
</Typography>
</CardContent>
<CardActions sx={{ justifyContent: 'center', flexDirection: 'column', pb: 3 }}>
<Link href={project.link.href} className="relative z-10 mt-2 flex items-center text-md font-semibold font-mono text-zinc-600 transition hover:text-[#00843D] dark:hover:text-yellow-400 dark:text-zinc-200">
<CardActions
sx={{ justifyContent: 'center', flexDirection: 'column', pb: 3 }}
>
<Link
href={project.link.href}
className="text-md relative z-10 mt-2 flex items-center font-mono font-semibold text-zinc-600 transition hover:text-[#00843D] dark:text-zinc-200 dark:hover:text-yellow-400"
>
<LinkIcon className="h-6 w-6 flex-none scale-110" />
<span className="ml-2">{project.link.label}</span>
</Link>
<div className="flex gap-6 mt-4">
<Link href={project.link.href} aria-label="GitHub" className="text-zinc-500 hover:text-black dark:text-zinc-400 dark:hover:text-white transition">
<FontAwesomeIcon icon={faGithub} className="w-6 h-6" />
</Link>
<Link href="https://discord.gg/hjUhu33uAn" aria-label="Discord" className="text-zinc-500 hover:text-[#5865F2] dark:text-zinc-400 dark:hover:text-[#5865F2] transition">
<FontAwesomeIcon icon={faDiscord} className="w-6 h-6" />
</Link>
</div>
<div className="mt-4 flex gap-6">
<Link
href={project.link.href}
aria-label="GitHub"
className="text-zinc-500 transition hover:text-black dark:text-zinc-400 dark:hover:text-white"
>
<FontAwesomeIcon icon={faGithub} className="h-6 w-6" />
</Link>
<Link
href="https://discord.gg/hjUhu33uAn"
aria-label="Discord"
className="text-zinc-500 transition hover:text-[#5865F2] dark:text-zinc-400 dark:hover:text-[#5865F2]"
>
<FontAwesomeIcon icon={faDiscord} className="h-6 w-6" />
</Link>
</div>
</CardActions>
</MuiCard>
</Grid>
))
}
</Grid >
);
};
))}
</Grid>
)
}

const ProjectSection = () => {
return (
<motion.div
<motion.div
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.8 }}
className="ideas-text flex items-center justify-center mb-8 relative"
className="ideas-text relative mb-8 flex items-center justify-center"
>
<div
className="hidden md:block w-[75px] h-[75px] m-2 bg-cover bg-center dark:bg-[url('/logo.png')] bg-[url('/logo.png')] absolute left-10"
className="absolute left-10 m-2 hidden h-[75px] w-[75px] bg-[url('/logo.png')] bg-cover bg-center dark:bg-[url('/logo.png')] md:block"
alt="GSOC Logo"
></div>

<h1 className="font-mono text-6xl font-extrabold tracking-tighter text-[#32a852] dark:text-yellow-400 sm:text-6xl md:text-5xl lg:text-6xl text-center">
<h1 className="text-center font-mono text-6xl font-extrabold tracking-tighter text-[#32a852] dark:text-yellow-400 sm:text-6xl md:text-5xl lg:text-6xl">
PROJECTS
</h1>

<div
className="hidden md:block w-[75px] h-[75px] m-2 bg-cover bg-center absolute right-10"
className="absolute right-10 m-2 hidden h-[75px] w-[75px] bg-cover bg-center md:block"
style={{ backgroundImage: "url('/logo.png')" }}
aria-label="Logo"
></div>

</motion.div>
);
};
)
}

const styles = {
bannerWrapper: {
width: '100%',
position: 'relative',
overflow: 'hidden',
},
};
}

export default function Projects() {
const readyToDownload = projects.filter(p => p.category === 'Ready to download');
const productionReady = projects.filter(p => p.category === 'Production ready');
const ongoing = projects.filter(p => p.category === 'Ongoing');
const readyToDownload = projects.filter(
(p) => p.category === 'Ready to download'
)
const productionReady = projects.filter(
(p) => p.category === 'Production ready'
)
const ongoing = projects.filter((p) => p.category === 'Ongoing')

return (
<>
<Container className="mt-20 mb-28">
<Container.Inner>
<div className="grid grid-cols-1 gap-x-8 gap-y-16 lg:grid-cols-3 mb-16">
{projects.sort(() => 0.5 - Math.random()).map((product) => (
<CardProduct key={product.slug} product={product} />
))}
<div className="mb-16 grid grid-cols-1 gap-x-8 gap-y-16 lg:grid-cols-3">
{projects
.slice()
.sort((a, b) => a.name.localeCompare(b.name))
.map((product) => (
<CardProduct key={product.slug} product={product} />
Comment on lines +184 to +188
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Use a guaranteed unique key for CardProduct.

src/helper/projects.js still contains entries without a slug, so key={product.slug} will produce repeated undefined keys for part of this list. That can cause React to reuse the wrong card instance during reconciliation.

Proposed fix
-              .map((product) => (
-                <CardProduct key={product.slug} product={product} />
+              .map((product) => (
+                <CardProduct
+                  key={product.slug ?? product.name}
+                  product={product}
+                />
               ))}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{projects
.slice()
.sort((a, b) => a.name.localeCompare(b.name))
.map((product) => (
<CardProduct key={product.slug} product={product} />
{projects
.slice()
.sort((a, b) => a.name.localeCompare(b.name))
.map((product) => (
<CardProduct
key={product.slug ?? product.name}
product={product}
/>
))}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/projects/page.jsx` around lines 184 - 188, The list uses product.slug
as the React key but some entries lack slugs, producing duplicate/undefined
keys; update the map rendering of CardProduct to use a guaranteed unique
identifier (prefer a stable unique field like product.id or product._id), and if
that field may be missing add a deterministic fallback (e.g. derived from
product.name combined with an index) so the key for each CardProduct in the
products.map callback is always unique and stable.

))}
</div>

{/*
Expand All @@ -180,7 +207,6 @@ export default function Projects() {
</div>
</div>
*/}

</Container.Inner>
</Container>

Expand All @@ -190,5 +216,5 @@ export default function Projects() {
</Container.Outer>
</div>
</>
);
)
}
Loading