);
}
@@ -28,13 +23,11 @@ function BlogCardSkeleton() {
export default function BlogLoading() {
return (
- {/* Page Header */}
-
-
+
+
- {/* Blog Grid */}
{Array.from({ length: 6 }).map((_, index) => (
diff --git a/src/app/[locale]/blog/page.tsx b/src/app/[locale]/blog/page.tsx
index 9ba6160..9951e97 100644
--- a/src/app/[locale]/blog/page.tsx
+++ b/src/app/[locale]/blog/page.tsx
@@ -4,6 +4,7 @@ import Script from "next/script";
import { getInitialBlogs } from "@/utils/blog";
import { getTranslations } from "next-intl/server";
+import { getLocalizedPath } from "@/config/i18n/navigation";
import BlogList from "@/components/common/blog/BlogList";
import ErrorBoundary from "@/components/common/error-boundary";
@@ -22,9 +23,9 @@ export async function generateMetadata({
title: pageTitle,
description: pageDescription,
alternates: {
- canonical: `/${locale}/blog`,
+ canonical: getLocalizedPath("/blog", locale),
languages: {
- en: "/en/blog",
+ en: "/blog",
bn: "/bn/blog",
hi: "/hi/blog",
es: "/es/blog",
@@ -33,7 +34,7 @@ export async function generateMetadata({
openGraph: {
title: pageTitle,
description: pageDescription,
- url: `/${locale}/blog`,
+ url: getLocalizedPath("/blog", locale),
siteName: "React Kolkata",
locale: locale,
type: "website",
diff --git a/src/app/[locale]/code-of-conduct/page.tsx b/src/app/[locale]/code-of-conduct/page.tsx
new file mode 100644
index 0000000..9265723
--- /dev/null
+++ b/src/app/[locale]/code-of-conduct/page.tsx
@@ -0,0 +1,131 @@
+import { Metadata } from "next";
+
+import { getLocalizedPath } from "@/config/i18n/navigation";
+
+export async function generateMetadata({
+ params,
+}: {
+ params: Promise<{ locale: string }>;
+}): Promise {
+ const { locale } = await params;
+
+ const pageTitle = "Code of Conduct";
+ const pageDescription =
+ "How we look out for each other at React Kolkata, at our events and online.";
+
+ return {
+ title: pageTitle,
+ description: pageDescription,
+ alternates: {
+ canonical: getLocalizedPath("/code-of-conduct", locale),
+ },
+ openGraph: {
+ title: pageTitle,
+ description: pageDescription,
+ url: getLocalizedPath("/code-of-conduct", locale),
+ siteName: "React Kolkata",
+ locale: locale,
+ type: "website",
+ },
+ twitter: {
+ card: "summary_large_image",
+ title: pageTitle,
+ description: pageDescription,
+ },
+ };
+}
+
+const sections = [
+ {
+ heading: "The short version",
+ body: [
+ "Be kind. That is really what it comes down to. React Kolkata exists so people can learn React, meet others, and have a good time doing it. Everyone who shows up, whether it is their first meetup or their fiftieth, should feel welcome here.",
+ ],
+ },
+ {
+ heading: "How we treat each other",
+ body: [
+ "We welcome people of every background, identity, and experience level. Beginners asking basic questions are just as important to this community as senior engineers giving talks.",
+ "Give people the benefit of the doubt, listen more than you speak, and remember that there is a human being on the other side of every conversation. A little patience goes a long way, especially with newcomers who are still finding their footing.",
+ ],
+ },
+ {
+ heading: "What is never okay",
+ body: [
+ "Harassment of any kind has no place here. That includes offensive comments about someone's gender, identity, appearance, or background, intimidation, unwelcome attention, and deliberately disrupting talks or discussions.",
+ "If someone tells you that something you said or did made them uncomfortable, please take it seriously. You do not have to agree or have a debate about it. Just stop, and move on gracefully.",
+ ],
+ },
+ {
+ heading: "At our events",
+ body: [
+ "Our meetups and workshops are shared spaces. Respect the speakers by listening while they talk and saving questions for the right moment. Respect the venue by cleaning up after yourself. And respect your fellow attendees by giving them space to participate too.",
+ "If you see someone sitting alone or looking a bit lost, say hello. Most of us remember what it felt like to walk into our first meetup, and a friendly face makes all the difference.",
+ ],
+ },
+ {
+ heading: "In our online spaces",
+ body: [
+ "The same rules apply on WhatsApp, Discord, GitHub, and social media. Keep discussions friendly and on topic, do not spam the groups, and do not share anyone's personal details or private messages without their permission.",
+ ],
+ },
+ {
+ heading: "If something feels off",
+ body: [
+ "If you experience or witness behaviour that does not sit right with you, please reach out to us at reactkolkata@gmail.com. Tell us what happened in your own words, and we will take it from there.",
+ "We will keep your message private and handle it with care. You will never get in trouble for raising a genuine concern.",
+ ],
+ },
+ {
+ heading: "What happens next",
+ body: [
+ "When a concern is reported, the organisers will look into it quietly and fairly. Depending on what happened, we might have a private word with the person involved, ask them to leave an event, or in serious cases, remove them from the community altogether.",
+ "Our goal is never punishment for its own sake. It is simply to keep React Kolkata a place where everyone feels safe showing up.",
+ ],
+ },
+];
+
+export default function CodeOfConductPage() {
+ return (
+
+
+
+
+
+
+
+
+ Code of Conduct
+
+
+ React Kolkata works when people feel safe asking questions, sharing unfinished ideas,
+ and being themselves. This page is our promise to keep it that way.
+
+
+
+
+
+ {sections.map((section) => (
+
+
+ {section.heading}
+
+ {section.body.map((paragraph) => (
+
+ {paragraph}
+
+ ))}
+
+ ))}
+
+
+
Thank you for being here.
+
+ Every friendly question answered, every talk given, and every newcomer welcomed is what
+ makes this community what it is. We are glad you are a part of it.
+
+
+ );
+}
diff --git a/src/app/[locale]/page.tsx b/src/app/[locale]/page.tsx
index 5fccd59..da71c65 100644
--- a/src/app/[locale]/page.tsx
+++ b/src/app/[locale]/page.tsx
@@ -1,6 +1,8 @@
import { Metadata } from "next";
import LandingPage from "@/modules/home";
+import { getLocalizedPath } from "@/config/i18n/navigation";
+
type Props = {
params: Promise<{ locale: string }>;
};
@@ -13,9 +15,9 @@ export async function generateMetadata({ params }: Props): Promise {
description:
"Join React Kolkata, the hub for React developers in Kolkata. Participate in meetups, workshops, and connect with the community to build, learn, and grow.",
alternates: {
- canonical: `/${locale}`,
+ canonical: getLocalizedPath("", locale),
languages: {
- en: "/en",
+ en: "/",
bn: "/bn",
hi: "/hi",
es: "/es",
@@ -25,7 +27,7 @@ export async function generateMetadata({ params }: Props): Promise {
title: "React Kolkata Community | Meetups, Events & Learning",
description:
"Connect with React developers in Kolkata. Join events, learn, and grow together.",
- url: `/${locale}`,
+ url: getLocalizedPath("", locale),
images: [
{
url: "/images/hero.jpeg",
diff --git a/src/app/sitemap.ts b/src/app/sitemap.ts
index a7fa924..9290c1c 100644
--- a/src/app/sitemap.ts
+++ b/src/app/sitemap.ts
@@ -1,25 +1,23 @@
import { MetadataRoute } from "next";
-import { routing } from "@/config/i18n/navigation";
+import { getLocalizedPath, routing } from "@/config/i18n/navigation";
const host = "https://reactkolkata.com";
export default function sitemap(): MetadataRoute.Sitemap {
- const routes = ["", "/events", "/contributors", "/blog"];
+ const routes = ["", "/events", "/contributors", "/blog", "/code-of-conduct", "/media-kit"];
return routes.map((route) => {
const alternates: { languages: Record } = {
languages: {},
};
- // Generate alternates for all supported locales
routing.locales.forEach((locale) => {
- alternates.languages[locale] = `${host}/${locale}${route}`;
+ alternates.languages[locale] = `${host}${getLocalizedPath(route, locale)}`;
});
return {
- // We use the default locale for the primary URL of the sitemap entry
- url: `${host}/${routing.defaultLocale}${route}`,
+ url: `${host}${getLocalizedPath(route, routing.defaultLocale)}`,
lastModified: new Date(),
changeFrequency: "weekly",
priority: route === "" ? 1 : 0.8,
diff --git a/src/base/constants/site/index.ts b/src/base/constants/site/index.ts
index 21efb78..e7d607a 100644
--- a/src/base/constants/site/index.ts
+++ b/src/base/constants/site/index.ts
@@ -54,14 +54,11 @@ export const highlights = [
},
];
-// storing constant to hit hashnode api call
export const HASHNODE_API_URL = "https://gql.hashnode.com/";
-// number of blogs to fetch in an api call
export const FETCH_BLOGS_COUNT_MOBILE = 4;
export const FETCH_BLOGS_COUNT_PAD = 6;
export const FETCH_BLOGS_COUNT_DESKTOP = 8;
-// number of character for author in blog card
export const AUTHOR_NAME_CHAR_LIMIT_MD = 10;
export const AUTHOR_NAME_CHAR_LIMIT_XL = 13;
diff --git a/src/base/data/dummy/index.ts b/src/base/data/dummy/index.ts
index 68150a4..f160683 100644
--- a/src/base/data/dummy/index.ts
+++ b/src/base/data/dummy/index.ts
@@ -8,64 +8,12 @@ export const events = [
description:
"Join us at the first-ever in-person meetup of React Kolkata. Calling out all the React and AI developers from the City of Joy and beyond to gather and make this event successful with learning and sharing.",
registrationUrl: "https://luma.com/event/evt-39ktH71VIdB6ynb",
- image:
- "https://images.lumacdn.com/cdn-cgi/image/format=auto,fit=cover,dpr=1,background=white,quality=75,width=400,height=400/event-covers/z2/8226021e-7f61-4c12-a7d1-e633d7b87163.jpg",
+ image: "/video/poster.mp4",
type: EVENT_TYPES.IN_PERSON,
startDateTime: "2026-05-24T10:00:00+05:30",
endDateTime: "2026-05-24T14:00:00+05:30",
isFeatured: true,
},
- {
- id: "rk-nov-2025",
- title: "Building Together: The React Kolkata Chapter by ReactPlay",
- venue: "Virtual",
- description:
- "If you’re passionate about React or any other exciting technology — whether it’s web development, cloud computing, AI, mobile apps, or something else — we invite you to be a speaker at our upcoming tech meetups!This event is open to everyone, whether you're just starting out or already deep into tech. Come by, learn something new, and be part of the conversation.",
- registrationUrl: "https://luma.com/eq8ntf1n",
- image:
- "https://images.lumacdn.com/cdn-cgi/image/format=auto,fit=cover,dpr=1,background=white,quality=75,width=400,height=400/event-covers/ma/ddb6a2c2-1af8-4e94-9d56-fd978b95ad39.png",
- type: EVENT_TYPES.ONLINE,
- startDateTime: "2025-11-29T19:00:00+05:30",
- endDateTime: "2025-11-29T21:30:00+05:30",
- },
- {
- id: "ze3qavyg",
- title: "CTRL + React: Join the React Kolkata Chapter by ReactPlay",
- venue: "Virtual",
- description:
- "In this event, our speakers will share insights on topics like React, AI, and other emerging tech. Expect practical takeaways, real-world examples, and fresh perspectives from folks working hands-on in the field. Event Schedule: Welcome Notes, Intro to Web3: Bringing Blockchain to Your React App - Aditya Singh, Context is All You Need - Akash Nath, React Router v7 Is Not as Scary as You Think - Elijah Asaolu, AI is easy only if you know how to crack it - Arindam Majumdar.",
- registrationUrl: "https://luma.com/ze3qavyg",
- image: "/images/tech-events-1.jpg",
- type: EVENT_TYPES.ONLINE,
- startDateTime: "2025-08-30T19:00:00+05:30",
- endDateTime: "2025-08-30T21:00:00+05:30",
- recordingUrl: "https://www.youtube.com/watch?v=Zvyc3UaTCxw",
- },
- {
- id: "sup8dxf3",
- title: "Speak Up, Tech Up! Join the React Kolkata Chapter by ReactPlay",
- venue: "Virtual",
- description:
- "In this event, our speakers will share insights on topics like React, AI, and other emerging tech. Expect practical takeaways, real-world examples, and fresh perspectives from folks working hands-on in the field. Event Schedule: Welcome notes, What is serverless inferencing in AI? - Haimantika Mitra, From JSX to the Cosmos: Navigating the React Ecosystem - Rohit Debnath, WebRTC for Frontend Devs Who've Suffered Enough Already - Sumanth, Break Time – Quick Quiz Inside, Bringing AI to your React Apps - Wadad Parker, Vibe Smart, Code Better: Things every react developer should know before vibe coding - Sayak Saha.",
- registrationUrl: "https://luma.com/sup8dxf3",
- image: "/images/tech-events-2.jpg",
- type: EVENT_TYPES.ONLINE,
- startDateTime: "2025-07-26T19:00:00+05:30",
- endDateTime: "2025-07-26T21:00:00+05:30",
- recordingUrl: "https://www.youtube.com/watch?v=mK2DNgrK21E",
- },
- {
- id: "9g6knhqp",
- title: "Hacktoberfest Special : Join the React Kolkata Chapter by ReactPlay",
- venue: "Virtual",
- description:
- "Join React Kolkata for an engaging panel discussion with open source maintainers and contributors as we gear up for Hacktoberfest. We'll talk about the realities of maintaining projects, the challenges of contribution, and how developers of all levels can meaningfully get involved in open source. Expect candid insights from experienced maintainers, practical tips for first-time contributors, and a chance to connect with the community that powers so much of the web. Whether you're curious about making your first PR or eager to share your journey, this is your space to learn, discuss, and be inspired.",
- registrationUrl: "https://luma.com/9g6knhqp",
- image: "/images/tech-events-3.jpg",
- type: EVENT_TYPES.ONLINE,
- startDateTime: "2025-10-11T19:00:00+05:30",
- endDateTime: "2025-10-11T21:00:00+05:30",
- },
];
export const articles = [
diff --git a/src/base/data/social-posts.ts b/src/base/data/social-posts.ts
new file mode 100644
index 0000000..aa7e0c0
--- /dev/null
+++ b/src/base/data/social-posts.ts
@@ -0,0 +1,137 @@
+export type MentionPlatform = "LinkedIn" | "X" | "Instagram";
+
+export type Mention = {
+ quote: string;
+ name: string;
+ meta: string;
+ context: string;
+ href: string;
+ platform: MentionPlatform;
+ postedAt?: string;
+ bio?: string;
+ avatar?: string;
+};
+
+export const bundledMentions: Mention[] = [
+ {
+ quote:
+ "A little late to the party with this post, but the energy from the August 23rd React Kolkata Community Meetup is still fresh! I had the absolute honor of being the Title Sponsor for this fantastic event. Connecting with such a vibrant and passionate community of developers, designers, and creators was truly inspiring. A major highlight for me was hosting the session, “Conquering the Blank Board: Brainstorming and Ideation in Miro.” Huge thanks to the React Kolkata team for organizing such a stellar meetup.",
+ name: "Milon Paul",
+ meta: "Title Sponsor · Aug'26 Meetup",
+ context: "82 reactions · #ReactKolkata",
+ href: "https://in.linkedin.com/in/milon-paul-92b798302",
+ platform: "LinkedIn",
+ postedAt: "2026-09-06",
+ bio: "Title Sponsor of the Aug'26 Meetup · hosted the “Conquering the Blank Board” Miro ideation session",
+ },
+ {
+ quote:
+ "Great tech events aren't just attended — they're experienced. Behind every successful developer meetup is an unspoken energy: heads down debugging together, animated debates in the aisles and ideas turning into code in real time. Recently documented the vibrant energy at the React Kolkata community meetup. Their motto — “Learn together, Grow together, Build together” — came alive in every corner of the room.",
+ name: "Gurdil Singh",
+ meta: "Event photographer",
+ context: "64 reactions · #reactkolkata",
+ href: "https://in.linkedin.com/in/gurdil-singh-05124b339",
+ platform: "LinkedIn",
+ postedAt: "2026-09-04",
+ bio: "Tech-event photographer documenting Kolkata meetups · @photographicgurdil_68",
+ },
+ {
+ quote:
+ "Thrilled to share that I attended the #ReactKolkata x #Hacktoberfest event! It was an incredible experience filled with insightful sessions — what an unforgettable day with amazing speakers, guests, and community.",
+ name: "Gunj Joshi",
+ meta: "Hacktoberfest attendee",
+ context: "#ReactKolkata",
+ href: "https://linkedin.com/in/gunjjoshi",
+ platform: "LinkedIn",
+ postedAt: "2026-08-30",
+ bio: "The D. E. Shaw Group · attended the #ReactKolkata x Hacktoberfest event",
+ },
+ {
+ quote:
+ "React Kolkata asked me to talk about React. I talked about React, WebAssembly, distributed systems, ghosting, commitment issues and somehow rizzed an entire room of developers. Then we turned their browsers into compute nodes. Frontend by day. Distributed systems by questionable life choice. Huge love to React Kolkata, Sulagna Ghosh and the entire team for the stage, the chaos and the trust. 10/10 event. 0/10 emotional stability.",
+ name: "Supratim Dhara",
+ meta: "Speaker · Aug'26 Meetup",
+ context: "#ReactKolkata",
+ href: "https://www.linkedin.com/feed/update/urn:li:activity:7500099508253650945/",
+ platform: "LinkedIn",
+ bio: "Co-Founder & CTO, bechoHub · spoke on React-powered compute networks",
+ },
+ {
+ quote:
+ "Had a great day at React Kolkata as a speaker for the Second Meetup of 2026 at Techno India University. Extending thanks to Sulagna Ghosh and team for organising the meetup so well and smoothly — it was really super cool. Worth mentioning really cool OG devs I met during the meetup: Sayak Saha, Supratim Dhara and Aprajita Verma — those few hours of interaction had more impact than the whole week! It was an awesome Sunday with Wiki Kolkata. Special thanks to Tapas Adhikary for such an awesome community you have built. #rtkaug26",
+ name: "Ayushman Bhattacharya",
+ meta: "Speaker · Aug'26 Meetup",
+ context: "#rtkaug26 · #ReactKolkata",
+ href: "https://www.linkedin.com/feed/update/urn:li:activity:7497530225099173888/",
+ platform: "LinkedIn",
+ bio: "Lead Organizer, GDG JIS University · spoke on the “use client” client boundary",
+ },
+ {
+ quote:
+ "From writing code to building with the community. Excited to be part of the React Kolkata Meetup: Aug'26 Edition at Techno India University, Kolkata. The event brings together developers, students, AI enthusiasts, founders, and tech professionals around one common goal: learning, building, and connecting. For me, events like this are not just about attending talks — they are opportunities to learn from people actually building. Learn together. Build together. Grow together.",
+ name: "Gourab Biswas",
+ meta: "Attendee · Aug'26 Meetup",
+ context: "#rtkaug26 · #ReactKolkata",
+ href: "https://www.linkedin.com/posts/gourab-biswas-478374291_rtkaug26-reactkolkata-reactjs-ugcPost-7497203023916621825-y3JP/",
+ platform: "LinkedIn",
+ bio: "Developer · attended the Aug'26 Edition at Techno India University",
+ },
+ {
+ quote:
+ "A great day of learning, connecting and building at the React Kolkata Meetup Aug'26 Edition! I'm really happy to have been a part of it, held at Techno India University, Kolkata. As a Computer Science student interested in software development and modern web technologies, attending developer meetups like this is always a great opportunity — it's not just about listening to technical talks, but also about meeting people, sharing ideas, asking questions, and understanding how others are learning and building.",
+ name: "Jeet Sasmal",
+ meta: "Attendee · Aug'26 Meetup",
+ context: "#ReactKolkata · #rtkaug26",
+ href: "https://www.linkedin.com/posts/jeet-sasmal-321958406_reactkolkata-rtkaug26-reactjs-ugcPost-7497345877045387264-TOLs/",
+ platform: "LinkedIn",
+ bio: "Computer Science student · attended at Techno India University",
+ },
+ {
+ quote:
+ "Second React Kolkata. Even bigger inspiration. Some events you attend. Some events make you want to build more, learn more and connect more. From deep technical discussions to the Buildathon, Quiz, networking and conversations with fellow developers — a perfect reminder that the tech community grows strongest when knowledge is shared. A special shoutout to the speakers and a very special shoutout to the React Kolkata organizing team for such a welcoming, high-energy developer community.",
+ name: "Sneha Chakraborty",
+ meta: "Two-time attendee · Aug'26 Meetup",
+ context: "#ReactKolkata",
+ href: "https://www.linkedin.com/posts/sneha-chakraborty-652978324_reactkolkata-reactjs-react-activity-7500014440843763712-1PEq",
+ platform: "LinkedIn",
+ bio: "Attended twice · from talks to Buildathon and Quiz",
+ },
+ {
+ quote:
+ "A new milestone in my tech journey! I successfully participated in the React Kolkata Buildathon, organized in collaboration with Miro Meetup and React Siliguri at Techno India University. Being part of this Buildathon was a great opportunity to learn, build, collaborate, and challenge myself in a real-world development environment. Grateful for the experience, the learning, and the connections made along the way.",
+ name: "Abhranil Dutta",
+ meta: "Buildathon participant · Aug'26",
+ context: "#ReactKolkata",
+ href: "https://www.linkedin.com/posts/abhranil-dutta_reactkolkata-buildathon-reactjs-activity-7500241051639693312-_7U2",
+ platform: "LinkedIn",
+ bio: "Built at the Miro × React Siliguri Buildathon",
+ },
+];
+
+const normalize = (item: Mention): Mention => ({
+ quote: item.quote,
+ name: item.name,
+ meta: typeof item.meta === "string" ? item.meta : "",
+ context: typeof item.context === "string" ? item.context : "",
+ href: item.href,
+ platform: item.platform ?? "LinkedIn",
+ postedAt: typeof item.postedAt === "string" ? item.postedAt : undefined,
+ bio: typeof item.bio === "string" ? item.bio : undefined,
+ avatar: typeof item.avatar === "string" ? item.avatar : undefined,
+});
+
+export function mergeMentions(): Mention[] {
+ const seen = new Set();
+ const merged: Mention[] = [];
+
+ const push = (item: Mention) => {
+ const key = item.href.trim();
+ if (!key || seen.has(key)) return;
+ seen.add(key);
+ merged.push(normalize(item));
+ };
+
+ for (const item of bundledMentions) push(item);
+
+ return merged.sort((a, b) => (b.postedAt ?? "").localeCompare(a.postedAt ?? ""));
+}
diff --git a/src/base/styles/globals.css b/src/base/styles/globals.css
index 9c6f2b7..c0b01a6 100644
--- a/src/base/styles/globals.css
+++ b/src/base/styles/globals.css
@@ -4,6 +4,8 @@
:root {
--font-geist-sans: "Geist Sans", ui-sans-serif, system-ui, -apple-system;
--font-geist-mono: "Geist Mono", ui-monospace, SFMono-Regular;
+ --font-display: "Instrument Serif", ui-serif, Georgia, serif;
+ --font-sans-body: "Inter", ui-sans-serif, system-ui, -apple-system, sans-serif;
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--card: oklch(1 0 0);
@@ -77,7 +79,7 @@
@theme inline {
--font-sans: Geist, "Geist Placeholder", sans-serif;
--font-mono: var(--font-geist-mono);
- /* optional: --font-sans, --font-serif, --font-mono if they are applied in the layout.tsx */
+ --font-display: var(--font-display);
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
@@ -123,7 +125,16 @@
transform: translateX(-50%);
}
}
- --animate-marquee: marquee 30s linear infinite;
+ --animate-marquee: marquee 45s linear infinite;
+ @keyframes marquee-reverse {
+ from {
+ transform: translateX(-50%);
+ }
+ to {
+ transform: translateX(0);
+ }
+ }
+ --animate-marquee-reverse: marquee-reverse 55s linear infinite;
}
@layer base {
@@ -145,11 +156,15 @@
body {
@apply bg-background text-foreground;
- font-family: Geist, "Geist Placeholder", sans-serif;
+ font-family: var(--font-sans-body), Geist, "Geist Placeholder", sans-serif;
}
}
@layer utilities {
+ .font-display {
+ font-family: var(--font-display);
+ }
+
/* Hide scrollbar for Chrome, Safari and Opera */
.no-scrollbar::-webkit-scrollbar {
display: none;
@@ -167,6 +182,19 @@
outline-color: rgb(56 189 248 / 0.8);
}
+/* Pause marquees on hover */
+.marquee-paused:hover .animate-marquee,
+.marquee-paused:hover .animate-marquee-reverse {
+ animation-play-state: paused;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .animate-marquee,
+ .animate-marquee-reverse {
+ animation: none;
+ }
+}
+
/* Ensure sections are visible on mobile */
@media (max-width: 768px) {
section {
diff --git a/src/components/common/blog/BlogCard.tsx b/src/components/common/blog/BlogCard.tsx
index 103a396..315b0ea 100644
--- a/src/components/common/blog/BlogCard.tsx
+++ b/src/components/common/blog/BlogCard.tsx
@@ -32,11 +32,10 @@ const BlogCard: React.FC = ({
const [modalOpen, setModalOpen] = useState(false);
const { isPad, isDesktop } = useDeviceDetail();
- // Validate required properties
if (!title || !publishedAt || !author || !author.name) {
console.error("BlogCard: Missing required properties", { title, publishedAt, author });
return (
-
+
- {/* Blog Modal */}
{
if (
!blog ||
@@ -51,9 +49,7 @@ export default function BlogList({
const [selectedTags, setSelectedTags] = useState([]);
const [showFilters, setShowFilters] = useState(false);
const [isPending, startTransition] = useTransition();
- const locale = useLocale();
- // Ref to track if a fetch is currently in progress (prevents race conditions)
const isFetchingRef = useRef(false);
const t = useTranslations("Blog");
@@ -65,11 +61,9 @@ export default function BlogList({
return FETCH_BLOGS_COUNT_DESKTOP;
}, [isMobile, isPad]);
- // Get all unique tags from articles
const allTags = useMemo(() => {
const tagMap = new Map();
if (blogsResponse.error) return [];
- // get blogs from data property of blogsResponse
const blogs = blogsResponse.data;
blogs.forEach((article) => {
article.tags.forEach((tag) => {
@@ -79,7 +73,6 @@ export default function BlogList({
return Array.from(tagMap.values());
}, [blogsResponse.data, blogsResponse.error]);
- // Filter articles based on selected tags
const filteredArticles = useMemo(() => {
if (blogsResponse.error) return [];
const blogs = blogsResponse.data;
@@ -100,12 +93,10 @@ export default function BlogList({
const handleMoreBlogsCTA = (e: React.MouseEvent) => {
e.preventDefault();
- // Prevent concurrent requests (race condition guard)
if (isFetchingRef.current || isPending) {
return;
}
- // Mark fetch as in progress
isFetchingRef.current = true;
loadMoreBlogs(cursor, blogsToShow())
@@ -116,7 +107,6 @@ export default function BlogList({
});
return;
}
- // Validate new blogs before adding
const validNewBlogs = newBlogs.filter((blog) => {
if (
!blog ||
@@ -135,7 +125,6 @@ export default function BlogList({
startTransition(() => {
setBlogsResponse((prev) => {
- // Additional safety: check for duplicate IDs before merging
const existingIds = new Set(prev.data.map((b) => b.id));
const uniqueNewBlogs = validNewBlogs.filter((blog) => !existingIds.has(blog.id));
@@ -156,7 +145,6 @@ export default function BlogList({
});
})
.finally(() => {
- // Always reset the fetch flag, even on error
isFetchingRef.current = false;
});
};
@@ -164,11 +152,11 @@ export default function BlogList({
return (
{blogsResponse.error ? (
-
+
{blogsResponse.error}
@@ -190,10 +178,10 @@ export default function BlogList({
{t("filter")}
- {/* Show only when we are not pres */}
+
{!showLoadMoreButton && (
{t("view_all_posts")}
@@ -202,9 +190,8 @@ export default function BlogList({