Skip to content
Closed
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
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
</p>

<p align="center">
<a href="https://your-site-url.com">
<img src="https://img.shields.io/badge/Live-Demo-success?style=for-the-badge" alt="Live Demo">
</a>
<a href="https://github.com/devpathindcommunity-india/DevPath-Web/actions/workflows/nextjs.yml">
<img src="https://img.shields.io/github/actions/workflow/status/devpathindcommunity-india/DevPath-Web/nextjs.yml?branch=master&style=for-the-badge" alt="CI Build Status">
</a>
<a href="https://github.com/devpathindcommunity-india/DevPath-Web/graphs/contributors"><img src="https://img.shields.io/github/contributors/devpathindcommunity-india/DevPath-Web.svg?style=for-the-badge" alt="Contributors"></a>
<a href="https://github.com/devpathindcommunity-india/DevPath-Web/network/members"><img src="https://img.shields.io/github/forks/devpathindcommunity-india/DevPath-Web.svg?style=for-the-badge" alt="Forks"></a>
<a href="https://github.com/devpathindcommunity-india/DevPath-Web/stargazers"><img src="https://img.shields.io/github/stars/devpathindcommunity-india/DevPath-Web.svg?style=for-the-badge" alt="Stargazers"></a>
Expand Down
1 change: 0 additions & 1 deletion next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@

const nextConfig: NextConfig = {
/* config options here */
output: 'export',
devIndicators: {
// @ts-ignore - buildActivity is valid but missing in type definition

Check failure on line 6 in next.config.ts

View workflow job for this annotation

GitHub Actions / ESLint & Prettier Check

Use "@ts-expect-error" instead of "@ts-ignore", as "@ts-ignore" will do nothing if the following line is error-free
buildActivity: false,
// @ts-ignore - appIsrStatus is valid but missing in type definition

Check failure on line 8 in next.config.ts

View workflow job for this annotation

GitHub Actions / ESLint & Prettier Check

Use "@ts-expect-error" instead of "@ts-ignore", as "@ts-ignore" will do nothing if the following line is error-free
appIsrStatus: false,
},
reactCompiler: false,
Expand Down
20 changes: 19 additions & 1 deletion src/app/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
import { useAuth } from '@/context/AuthContext';
import { useNotificationActions } from '@/stores/ui-store';
import { useMaintenance } from '@/hooks/useMaintenance';
import { auth, db } from '@/lib/firebase';
import { auth, db, isFirebaseConfigured } from '@/lib/firebase';

export default function LoginPage() {
const [email, setEmail] = useState('');
Expand Down Expand Up @@ -113,6 +113,15 @@
setIsCheckingAdmin(true);

try {
if (!isFirebaseConfigured) {
const message =
'Login is temporarily unavailable because Firebase is not configured.';
setError(message);
showError(message);
setIsCheckingAdmin(false);
return;
}

await login(normalizedEmail, password);
const adminDoc = await getDoc(doc(db, 'admins', normalizedEmail));

Expand All @@ -126,7 +135,7 @@
setIsCheckingAdmin(false);
router.push('/profile');
}
} catch (err: any) {

Check failure on line 138 in src/app/login/page.tsx

View workflow job for this annotation

GitHub Actions / ESLint & Prettier Check

Unexpected any. Specify a different type
console.error(err);

const nextAttempts = failedAttempts + 1;
Expand Down Expand Up @@ -168,6 +177,15 @@
setIsCheckingAdmin(true);

try {
if (!isFirebaseConfigured) {
const message =
'Sign-in is temporarily unavailable because Firebase is not configured.';
setError(message);
showError(message);
setIsCheckingAdmin(false);
return;
}

const provider =
providerName === 'google'
? new GoogleAuthProvider()
Expand Down Expand Up @@ -204,7 +222,7 @@
setIsCheckingAdmin(false);
router.push('/profile');
}
} catch (err: any) {

Check failure on line 225 in src/app/login/page.tsx

View workflow job for this annotation

GitHub Actions / ESLint & Prettier Check

Unexpected any. Specify a different type
console.error(err);
const message =
err?.code === 'auth/popup-closed-by-user'
Expand Down Expand Up @@ -485,7 +503,7 @@

<div className="mt-6 flex flex-col gap-4 text-center text-sm text-muted-foreground sm:flex-row sm:items-center sm:justify-between sm:text-left">
<p>
Don't have an account?{' '}

Check failure on line 506 in src/app/login/page.tsx

View workflow job for this annotation

GitHub Actions / ESLint & Prettier Check

`'` can be escaped with `&apos;`, `&lsquo;`, `&#39;`, `&rsquo;`
<Link
href="/signup"
className="font-medium text-cyan-300 transition-colors hover:text-cyan-200"
Expand Down
41 changes: 37 additions & 4 deletions src/app/profile/[username]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,48 @@

import { Metadata } from 'next';
import { notFound } from 'next/navigation';
import { collection, getDocs, query, where } from 'firebase/firestore';
import { getPublicProfileByUsername } from '@/lib/portfolio-service';
import { db, isFirebaseConfigured } from '@/lib/firebase';
import { ProfileHeader } from '@/components/profile/ProfileHeader';
import { PathProgressSection } from '@/components/profile/PathProgressSection';
import { SkillBadgesSection } from '@/components/profile/SkillBadgesSection';
import { ProjectShowcaseSection } from '@/components/profile/ProjectShowcaseSection';
import { ExportBar } from '@/components/profile/ExportBar';

interface Props {

Check warning on line 15 in src/app/profile/[username]/page.tsx

View workflow job for this annotation

GitHub Actions / ESLint & Prettier Check

'Props' is defined but never used
params: { username: string };
}

export const dynamicParams = false;

export async function generateStaticParams() {
if (!isFirebaseConfigured) return [];

try {
const profilesQuery = query(
collection(db, 'portfolios'),
where('isPublic', '==', true)
);
const snapshot = await getDocs(profilesQuery);

return snapshot.docs
.map((profileDoc) => profileDoc.data().username)
.filter((username): username is string => Boolean(username))
.map((username) => ({ username }));
} catch {
return [];
}
}

// Generate OpenGraph metadata dynamically
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const profile = await getPublicProfileByUsername(params.username);
export async function generateMetadata({
params,
}: {
params: Promise<{ username: string }>;
}): Promise<Metadata> {
const { username } = await params;
const profile = await getPublicProfileByUsername(username);
if (!profile) return { title: 'Profile not found' };

return {
Expand All @@ -30,8 +58,13 @@
};
}

export default async function PublicProfilePage({ params }: Props) {
const profile = await getPublicProfileByUsername(params.username);
export default async function PublicProfilePage({
params,
}: {
params: Promise<{ username: string }>;
}) {
const { username } = await params;
const profile = await getPublicProfileByUsername(username);

if (!profile) notFound();

Expand Down
64 changes: 25 additions & 39 deletions src/lib/firebase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,67 +3,53 @@ import { getAnalytics, isSupported } from 'firebase/analytics';
import { getFirestore, Firestore } from 'firebase/firestore';
import { getAuth, Auth } from 'firebase/auth';

const isProduction = process.env.NODE_ENV === 'production';

const firebaseConfig = {
apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID,
};

// Production Safety Guard
if (isProduction) {
const missingKeys = Object.entries(firebaseConfig)
.filter(([_, value]) => !value)
.map(([key]) => key);

if (missingKeys.length > 0) {
throw new Error(
`Production Deployment Error: Missing required Firebase environment variables: ${missingKeys.join(', ')}`
);
}
} else {
// Safe Mock Fallbacks strictly for local development/testing only
firebaseConfig.apiKey =
firebaseConfig.apiKey || 'mock-api-key-for-local-testing-only-123456';
firebaseConfig.authDomain =
firebaseConfig.authDomain || 'mock-app.firebaseapp.com';
firebaseConfig.projectId = firebaseConfig.projectId || 'mock-app-id';
firebaseConfig.storageBucket =
firebaseConfig.storageBucket || 'mock-app.appspot.com';
firebaseConfig.messagingSenderId =
firebaseConfig.messagingSenderId || '1234567890';
}

const isFirebaseConfigValid = Boolean(
const isFirebaseConfigured = Boolean(
firebaseConfig.apiKey &&
firebaseConfig.authDomain &&
firebaseConfig.projectId &&
firebaseConfig.storageBucket &&
firebaseConfig.messagingSenderId
firebaseConfig.messagingSenderId &&
firebaseConfig.appId
);

const app: FirebaseApp | null = isFirebaseConfigValid
const app: FirebaseApp | null = isFirebaseConfigured
? !getApps().length
? initializeApp(firebaseConfig)
: getApp()
: null;

if (!app) {
console.warn(
'Firebase is not configured. Running in local UI-only mode without Firebase auth or Firestore.'
);
}

// For build-time typing we export `db` as a Firestore instance. At runtime
// `app` may be null when Firebase isn't configured; in that case we provide
// a minimal cast to satisfy TypeScript while keeping runtime behavior safe.
const db: Firestore = app
? (getFirestore(app) as Firestore)
: (null as unknown as Firestore);
const auth: Auth = app ? (getAuth(app) as Auth) : ({} as Auth);
const firebaseAvailable = Boolean(app);

export { db, auth, firebaseAvailable, firebaseConfig };
let analytics;

if (app && typeof window !== 'undefined') {
isSupported().then((supported) => {
if (supported) {
analytics = getAnalytics(app);
}
});
}

export {
app,
analytics,
db,
auth,
firebaseAvailable,
firebaseConfig,
isFirebaseConfigured,
};
Loading