fix: replace img tags with next/image, fix useEffect deps, suppress test mock warnings
All checks were successful
CI / CD / test-build (push) Successful in 11m2s
CI / CD / deploy-dev (push) Successful in 1m4s
CI / CD / deploy-production (push) Has been skipped

- projects/page.tsx & projects/[slug]/page.tsx: <img> → <Image fill unoptimized>
- ActivityFeed.tsx: add allQuotes.length to useEffect deps
- Test mocks: eslint-disable for intentional <img> in next/image mocks

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-03-04 15:40:19 +01:00
parent d80c936c60
commit 10a545f014
5 changed files with 11 additions and 3 deletions

View File

@@ -11,6 +11,7 @@ jest.mock("next-intl", () => ({
// Mock next/image // Mock next/image
jest.mock("next/image", () => ({ jest.mock("next/image", () => ({
__esModule: true, __esModule: true,
// eslint-disable-next-line @next/next/no-img-element
default: (props: React.ImgHTMLAttributes<HTMLImageElement>) => <img {...props} alt={props.alt || ""} />, default: (props: React.ImgHTMLAttributes<HTMLImageElement>) => <img {...props} alt={props.alt || ""} />,
})); }));

View File

@@ -30,6 +30,7 @@ interface ImageProps {
jest.mock('next/image', () => ({ jest.mock('next/image', () => ({
__esModule: true, __esModule: true,
default: ({ src, alt, fill, priority, ...props }: ImageProps) => ( default: ({ src, alt, fill, priority, ...props }: ImageProps) => (
// eslint-disable-next-line @next/next/no-img-element
<img <img
src={src} src={src}
alt={alt} alt={alt}

View File

@@ -110,7 +110,7 @@ export default function ActivityFeed({
clearInterval(statusInterval); clearInterval(statusInterval);
clearInterval(quoteInterval); clearInterval(quoteInterval);
}; };
}, [onActivityChange]); }, [onActivityChange, allQuotes.length]);
if (loading) { if (loading) {
return <div className="animate-pulse space-y-4"> return <div className="animate-pulse space-y-4">

View File

@@ -2,6 +2,7 @@
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import { ExternalLink, Calendar, ArrowLeft, Github as GithubIcon, Share2 } from 'lucide-react'; import { ExternalLink, Calendar, ArrowLeft, Github as GithubIcon, Share2 } from 'lucide-react';
import Image from 'next/image';
import Link from 'next/link'; import Link from 'next/link';
import { useParams } from 'next/navigation'; import { useParams } from 'next/navigation';
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
@@ -132,9 +133,11 @@ const ProjectDetail = () => {
className="mb-16 rounded-2xl overflow-hidden shadow-2xl bg-stone-100 aspect-video relative" className="mb-16 rounded-2xl overflow-hidden shadow-2xl bg-stone-100 aspect-video relative"
> >
{project.imageUrl ? ( {project.imageUrl ? (
<img <Image
src={project.imageUrl} src={project.imageUrl}
alt={project.title} alt={project.title}
fill
unoptimized
className="w-full h-full object-cover" className="w-full h-full object-cover"
/> />
) : ( ) : (

View File

@@ -3,6 +3,7 @@
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import { ExternalLink, Github, Calendar, ArrowLeft, Search } from 'lucide-react'; import { ExternalLink, Github, Calendar, ArrowLeft, Search } from 'lucide-react';
import Image from 'next/image';
import Link from 'next/link'; import Link from 'next/link';
import { useLocale, useTranslations } from "next-intl"; import { useLocale, useTranslations } from "next-intl";
@@ -159,9 +160,11 @@ const ProjectsPage = () => {
<div className="relative aspect-[16/10] overflow-hidden bg-stone-100"> <div className="relative aspect-[16/10] overflow-hidden bg-stone-100">
{project.imageUrl ? ( {project.imageUrl ? (
<> <>
<img <Image
src={project.imageUrl} src={project.imageUrl}
alt={project.title} alt={project.title}
fill
unoptimized
className="w-full h-full object-cover transition-transform duration-1000 ease-out group-hover:scale-110" className="w-full h-full object-cover transition-transform duration-1000 ease-out group-hover:scale-110"
/> />
<div className="absolute inset-0 bg-gradient-to-t from-stone-900/20 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500" /> <div className="absolute inset-0 bg-gradient-to-t from-stone-900/20 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500" />