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
3 changes: 3 additions & 0 deletions app/blog/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ export const metadata: Metadata = {
title: 'Blog | Frontend Development Insights & Tutorials',
description:
'Deep dives into frontend development, performance optimization, React patterns, CSS techniques, and industry best practices from the Frontend Junction community.',
alternates: {
canonical: 'https://www.frontend-junction.com/blog',
},
};

const POSTS_PER_PAGE = 6; // Changed to 6 for better grid (2x3)
Expand Down
6 changes: 4 additions & 2 deletions app/companies/[company]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import { Metadata } from 'next';
import Script from 'next/script';
import { FaCalendar, FaUser } from 'react-icons/fa';

export const dynamic = 'force-dynamic';
export const revalidate = 600; // Cache for 10 minutes
export const revalidate = 600; // ISR: revalidate every 10 minutes

interface Props {
params: Promise<{ company: string }>;
Expand All @@ -32,6 +31,9 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
`${companyName} software engineer interview`,
`${companyName} react interview`,
],
alternates: {
canonical: `https://www.frontend-junction.com/companies/${company}`,
},
openGraph: {
title: `${companyName} Frontend Interview Experiences`,
description: `Browse real ${companyName} frontend engineer interview experiences.`,
Expand Down
6 changes: 4 additions & 2 deletions app/companies/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import Link from 'next/link';
import { Metadata } from 'next';
import Script from 'next/script';

export const dynamic = 'force-dynamic';
export const revalidate = 3600; // Cache for 1 hour
export const revalidate = 3600; // ISR: revalidate every 1 hour

export const metadata: Metadata = {
title: 'Top Companies for Frontend Interviews | Frontend Junction',
description:
'Browse frontend interview experiences by company. Prepare for your next interview at Google, Amazon, Uber, and more.',
alternates: {
canonical: 'https://www.frontend-junction.com/companies',
},
openGraph: {
title: 'Top Companies for Frontend Interviews',
description:
Expand Down
36 changes: 36 additions & 0 deletions app/error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
'use client';

import Link from 'next/link';

export default function Error({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
return (
<div className='container mx-auto flex min-h-[60vh] flex-col items-center justify-center px-4 text-center'>
<h1 className='text-5xl font-black text-destructive mb-4'>Oops!</h1>
<h2 className='text-xl font-bold mb-2'>Something went wrong</h2>
<p className='text-muted-foreground mb-8 max-w-md'>
An unexpected error occurred. Please try again or navigate to another
page.
</p>
<div className='flex flex-wrap gap-4 justify-center'>
<button
onClick={reset}
className='px-6 py-3 rounded-lg bg-primary text-primary-foreground font-semibold hover:opacity-90 transition-opacity'
>
Try Again
</button>
<Link
href='/'
className='px-6 py-3 rounded-lg border border-border font-semibold hover:bg-accent transition-colors'
>
Go Home
</Link>
</div>
</div>
);
}
6 changes: 3 additions & 3 deletions app/interview-experience/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getExperienceBySlug } from '@/lib/getExperienceBySlug';
import { sanitizeHtml } from '@/lib/sanitize-html';
import { notFound } from 'next/navigation';
import Link from 'next/link';
import { FaExternalLinkAlt, FaCalendar, FaUser } from 'react-icons/fa';
Expand All @@ -7,8 +8,7 @@ import ReactMarkdown from 'react-markdown';
import ViewCounter from '@/components/view-counter';
import Script from 'next/script';

export const dynamic = 'force-dynamic';
export const revalidate = 60; // ISR
export const revalidate = 60; // ISR: revalidate every 60 seconds

interface Props {
params: Promise<{ slug: string }>;
Expand Down Expand Up @@ -202,7 +202,7 @@ export default async function ExperienceSlugPage({ params }: Props) {
{/* Content Analysis (AI Generated or Raw) */}
<div className='prose prose-lg dark:prose-invert max-w-none prose-headings:font-bold prose-headings:text-foreground prose-p:text-muted-foreground prose-strong:text-foreground prose-a:text-primary hover:prose-a:underline prose-img:rounded-xl'>
{experience.content?.trim().startsWith('<') ? (
<div dangerouslySetInnerHTML={{ __html: experience.content }} />
<div dangerouslySetInnerHTML={{ __html: sanitizeHtml(experience.content) }} />
) : (
<ReactMarkdown>
{experience.content || experience.summary || ''}
Expand Down
16 changes: 9 additions & 7 deletions app/interview-experience/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,23 @@ import { Suspense } from 'react';
import Loading from '../loading';

export const dynamic = 'force-dynamic';
export const revalidate = 0;

export const metadata: Metadata = {
title: 'Interview Experience',
title: 'Frontend Interview Experiences | Real Stories from Top Companies',
description:
'Browse through real front-end interview experiences from candidates at various companies. Get insights, tips, and prepare better for your next interview.',
'Browse 400+ real frontend interview experiences from Google, Amazon, Meta, Flipkart, and more. Get insights, tips, and prepare better for your next interview.',
keywords:
'front-end interview experiences, front-end developer interviews, front-end interview preparation, front-end interview insights, front-end interview stories',
'frontend interview experiences, frontend developer interviews, frontend interview preparation, frontend interview insights, frontend interview stories',
alternates: {
canonical: 'https://www.frontend-junction.com/interview-experience',
},
openGraph: {
type: 'website',
url: 'https://www.frontend-junction.com/interview-experience',
title: 'Front-end Interview Experiences | Front-end Junction',
title: 'Frontend Interview Experiences | Real Stories from Top Companies',
description:
'Browse through real front-end interview experiences from candidates at various companies. Get insights, tips, and prepare better for your next interview.',
siteName: 'Front-end Junction',
'Browse 400+ real frontend interview experiences from Google, Amazon, Meta, Flipkart, and more.',
siteName: 'Frontend Junction',
},
};

Expand Down
24 changes: 10 additions & 14 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import './globals.css';
import type { Metadata } from 'next';
import { Inter } from 'next/font/google';
import { cn } from '@/lib/utils';
import { SiteHeader } from '@/components/common/site-header';
import { ThemeProvider } from '@/components/common/theme-provider';
Expand All @@ -13,6 +14,12 @@ import {
} from '@/components/structured-data';
import { Analytics } from '@vercel/analytics/next';

const inter = Inter({
subsets: ['latin'],
display: 'swap',
variable: '--font-inter',
});

export const metadata: Metadata = {
metadataBase: new URL('https://www.frontend-junction.com'),
title: {
Expand Down Expand Up @@ -99,30 +106,19 @@ export default function RootLayout({
children: React.ReactNode;
}) {
return (
<html lang='en' suppressHydrationWarning>
<html lang='en' suppressHydrationWarning className={inter.variable}>
<head>
<meta name='google-adsense-account' content='ca-pub-4467873688771542' />
<Script
id='google-adsense'
strategy='beforeInteractive'
strategy='lazyOnload'
async
src='https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-4467873688771542'
crossOrigin='anonymous'
/>
{/* Structured Data */}
<OrganizationSchema />
<WebsiteSchema />
<link rel='preconnect' href='https://fonts.googleapis.com' />
<link
rel='preconnect'
href='https://fonts.gstatic.com'
crossOrigin='anonymous'
/>
{/* eslint-disable-next-line @next/next/no-page-custom-font */}
<link
href='https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap'
rel='stylesheet'
/>
</head>
{process.env.NEXT_GOOGLE_ANALYTICS && (
<Script
Expand All @@ -144,7 +140,7 @@ export default function RootLayout({
}}
/>
)}
<body className={cn('font-sans antialiased')}>
<body className={cn('font-sans antialiased', inter.className)}>
{/* Skip to main content - Accessibility */}
<a
href='#main-content'
Expand Down
40 changes: 40 additions & 0 deletions app/not-found.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import Link from 'next/link';
import { Metadata } from 'next';

export const metadata: Metadata = {
title: 'Page Not Found | Frontend Junction',
description: 'The page you are looking for does not exist.',
};

export default function NotFound() {
return (
<div className='container mx-auto flex min-h-[60vh] flex-col items-center justify-center px-4 text-center'>
<h1 className='text-6xl font-black text-primary mb-4'>404</h1>
<h2 className='text-2xl font-bold mb-2'>Page Not Found</h2>
<p className='text-muted-foreground mb-8 max-w-md'>
The page you&apos;re looking for doesn&apos;t exist or has been moved.
Check out our latest interview experiences or blog posts instead.
</p>
<div className='flex flex-wrap gap-4 justify-center'>
<Link
href='/interview-experience'
className='px-6 py-3 rounded-lg bg-primary text-primary-foreground font-semibold hover:opacity-90 transition-opacity'
>
Browse Experiences
</Link>
<Link
href='/blog'
className='px-6 py-3 rounded-lg border border-border font-semibold hover:bg-accent transition-colors'
>
Read Blog
</Link>
<Link
href='/companies'
className='px-6 py-3 rounded-lg border border-border font-semibold hover:bg-accent transition-colors'
>
View Companies
</Link>
</div>
</div>
);
}
28 changes: 23 additions & 5 deletions app/sitemap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ export default async function sitemap(): Promise<MetadataRoute.Sitemap> {

try {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY;
// Use service role key if available, fall back to anon key
const supabaseKey =
process.env.SUPABASE_SERVICE_ROLE_KEY ||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;

// Fetch Companies
const companies = await getCompanies();
Expand All @@ -67,12 +70,17 @@ export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const supabase = createClient(supabaseUrl, supabaseKey);

// Fetch scraped experiences with slugs
const { data: scrapedExperiences } = await supabase
const { data: scrapedExperiences, error: scrapedError } = await supabase
.from('scraped_experiences')
.select('slug, updated_at')
.eq('status', 'approved')
.not('slug', 'is', null)
.order('updated_at', { ascending: false })
.limit(500);
.limit(1000);

if (scrapedError) {
console.error('[Sitemap] Error fetching scraped experiences:', scrapedError.message);
}

if (scrapedExperiences) {
experiencePages = scrapedExperiences.map((exp) => ({
Expand All @@ -84,12 +92,16 @@ export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
}

// Fetch user-submitted experiences
const { data: userExperiences } = await supabase
const { data: userExperiences, error: userError } = await supabase
.from('new_interview')
.select('slug, updated_at')
.not('slug', 'is', null)
.order('updated_at', { ascending: false })
.limit(200);
.limit(500);

if (userError) {
console.error('[Sitemap] Error fetching user experiences:', userError.message);
}

if (userExperiences) {
experiencePages = [
Expand All @@ -102,6 +114,12 @@ export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
})),
];
}

console.log(
`[Sitemap] Generated ${experiencePages.length} experience URLs, ${companyPages.length} company URLs, ${blogPages.length} blog URLs`
);
} else {
console.warn('[Sitemap] Missing Supabase credentials — experience pages will not be included');
}
} catch (error) {
console.error('[Sitemap] Error fetching data:', error);
Expand Down
8 changes: 6 additions & 2 deletions app/tags/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@ import { posts } from '#site/content';
import { Tag } from '@/components/tag';

export const metadata: Metadata = {
title: 'Tags',
description: "Topic I've written about",
title: 'Tags | Browse All Frontend Topics',
description:
'Browse all frontend development topics — React, JavaScript, CSS, Next.js, TypeScript, and more. Find articles and guides by tag.',
alternates: {
canonical: 'https://www.frontend-junction.com/tags',
},
};

export default async function TagsPage() {
Expand Down
2 changes: 1 addition & 1 deletion components/add-new-experience.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ const InterviewExperienceForm: React.FC = () => {
setFormSubmitAttempted(true);

if (validateForm()) {
console.log('Form submitted:', formData);

setIsLoading(true); // Set loading to true when the request starts

try {
Expand Down
4 changes: 2 additions & 2 deletions components/common/footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export default function FooterComponent() {
<h4 className='font-semibold mb-4 text-foreground'>Community</h4>
<div className='flex gap-4'>
<a
href='https://github.com/depaksharma'
href='https://github.com/deepu0'
target='_blank'
rel='noopener noreferrer'
className='text-muted-foreground hover:text-white transition-colors'
Expand All @@ -72,7 +72,7 @@ export default function FooterComponent() {
<FaGithub size={20} />
</a>
<a
href='https://twitter.com'
href='https://twitter.com/frontendjunction'
target='_blank'
rel='noopener noreferrer'
className='text-muted-foreground hover:text-[#1DA1F2] transition-colors'
Expand Down
2 changes: 1 addition & 1 deletion components/session-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
const {
data: { subscription },
} = supabase.auth.onAuthStateChange(async (event: any, newSession: any) => {
console.log('[Auth] State changed:', event);


if (!mounted) return;

Expand Down
7 changes: 4 additions & 3 deletions config/site.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
export const siteConfig = {
name: 'Frontend Junction',
url: 'https://www.frontend-junction.com/',
description: 'Interview experience frontend, Frontend interview preparation',
author: 'Sharmag',
description:
'Your hub for frontend interview experiences, preparation guides, and developer jobs at top tech companies.',
author: 'Deepak Sharma',
links: {
twitter: 'https://twitter.com/sharmag',
twitter: 'https://twitter.com/frontendjunction',
github: 'https://github.com/deepu0',
linkedin: 'https://www.linkedin.com/in/depaksharma/',
},
Expand Down
Loading
Loading