Skip to content

Commit

Permalink
Merge pull request #122 from ArhanAnsari/ArhanAnsari-patch-1
Browse files Browse the repository at this point in the history
Update page.tsx
  • Loading branch information
ArhanAnsari authored Nov 2, 2024
2 parents 1644587 + 4dcbcc3 commit 8082c14
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 192 deletions.
28 changes: 0 additions & 28 deletions app/api/getUserData.ts

This file was deleted.

23 changes: 23 additions & 0 deletions app/api/getUserData/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//app/api/getUserData/route.ts
import { NextResponse } from "next/server";
import { adminDb } from "@/firebaseAdmin";

export async function GET(req: Request) {
const { searchParams } = new URL(req.url);
const email = searchParams.get("email");

if (!email) {
return NextResponse.json({ message: "Invalid email parameter" }, { status: 400 });
}

try {
const userDoc = await adminDb.collection("users").doc(email).get();
if (!userDoc.exists) {
return NextResponse.json({ message: "User not found" }, { status: 404 });
}
return NextResponse.json(userDoc.data(), { status: 200 });
} catch (error) {
console.error("Error fetching user data:", error);
return NextResponse.json({ message: "Internal Server Error" }, { status: 500 });
}
}
19 changes: 0 additions & 19 deletions app/api/updateUserData.ts

This file was deleted.

19 changes: 19 additions & 0 deletions app/api/updateUserData/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//app/api/updateUserData/route.ts
import { NextResponse } from "next/server";
import { adminDb } from "@/firebaseAdmin";

export async function POST(req: Request) {
try {
const { email, userData } = await req.json();

if (!email || !userData) {
return NextResponse.json({ message: "Invalid request data" }, { status: 400 });
}

await adminDb.collection("users").doc(email).set(userData, { merge: true });
return NextResponse.json({ message: "User data updated successfully" }, { status: 200 });
} catch (error) {
console.error("Error updating user data:", error);
return NextResponse.json({ message: "Internal Server Error" }, { status: 500 });
}
}
211 changes: 67 additions & 144 deletions app/profile/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,20 @@
"use client";

import { useState, useEffect } from "react";
import {
getAuth,
signOut,
updateProfile,
User,
linkWithPopup,
GoogleAuthProvider,
GithubAuthProvider,
} from "firebase/auth";
import { useSession, signIn } from "next-auth/react";
import { getConnectedProviders, connectProvider } from "@/firebaseFunctions";
import { getAuth, signOut, updateProfile, User } from "firebase/auth";
import { getUserData, updateUserData } from "@/firebaseFunctions"; // Custom Firebase functions
import { useSession, signIn, getProviders } from "next-auth/react";

// UserData interface including the 'name' property
interface UserData {
plan: "free" | "pro" | "enterprise";
requestCount: number;
name?: string;
name?: string; // Ensure 'name' is defined as optional
}

interface Provider {
id: string;
name: string;
}

const ProfilePage = () => {
Expand All @@ -28,30 +26,23 @@ const ProfilePage = () => {
const [name, setName] = useState<string>("");
const [nameEditMode, setNameEditMode] = useState<boolean>(false);
const [connectedProviders, setConnectedProviders] = useState<string[]>([]);
const [availableProviders, setAvailableProviders] = useState<Record<string, Provider> | null>(null);
const auth = getAuth();

useEffect(() => {
const fetchUserData = async () => {
if (!session) return;
if (!session) return;

const fetchUserData = async () => {
const currentUser = auth.currentUser;
if (currentUser) {
setUser(currentUser);
setName(currentUser.displayName || "");

try {
// Fetch user data from the API
const response = await fetch(
`${process.env.NEXT_PUBLIC_API_URL}/api/getUserData?email=${currentUser.email}`
);
const data = await response.json();
setUserData(
data ?? { plan: "free", requestCount: 0, name: currentUser.displayName || "" }
);

// Fetch connected providers
const providers = await getConnectedProviders(currentUser.email!);
setConnectedProviders(providers);
const data = await getUserData(currentUser.email!);
setUserData(data ?? { plan: "free", requestCount: 0, name: currentUser.displayName || "" });
const providers = await getProviders();
setAvailableProviders(providers);
} catch (error) {
console.error("Error fetching profile data:", error);
}
Expand All @@ -76,13 +67,14 @@ const ProfilePage = () => {

try {
await updateProfile(user, { displayName: name });
// Update user data using the API
await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/updateUserData`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email: user.email!, userData: { ...userData, name } }),
});
setUserData((prev) => (prev ? { ...prev, name } : prev));
// Ensure the updated userData includes 'name'
const updatedUserData = {
...userData,
name,
};

// Update userData in Firestore
await updateUserData(user.email!, updatedUserData);
setNameEditMode(false);
alert("Name updated successfully!");
} catch (error) {
Expand All @@ -91,133 +83,64 @@ const ProfilePage = () => {
}
};

const handleSignOut = () => {
signOut(auth).catch((error) => console.error("Sign out error:", error));
};

const handleProviderLink = async (provider: "google" | "github") => {
if (!user) return;

const providerInstance =
provider === "google" ? new GoogleAuthProvider() : new GithubAuthProvider();

const handleProviderLink = async (providerId: string) => {
if (!session) return;
try {
await linkWithPopup(user, providerInstance);
await connectProvider(user.email!, provider);
alert(
`${provider.charAt(0).toUpperCase() + provider.slice(1)} has been successfully linked to your account.`
);
setConnectedProviders((prev) => [...prev, provider]);
} catch (error: unknown) {
if (
typeof error === "object" &&
error !== null &&
"code" in error &&
(error as { code?: string }).code === "auth/credential-already-in-use"
) {
alert("This account is already linked to your current profile.");
} else {
console.error("Error linking provider:", error);
alert("Failed to link provider. Please try again.");
}
await signIn(providerId, { callbackUrl: "/profile" });
alert(`${providerId.charAt(0).toUpperCase() + providerId.slice(1)} has been successfully linked to your account.`);
setConnectedProviders((prev) => [...prev, providerId]);
} catch (error) {
console.error("Error linking provider:", error);
alert("Failed to link provider. Please try again.");
}
};

if (loading) {
return <div className="text-center text-lg mt-20">Loading...</div>;
return <div>Loading...</div>;
}

if (!session) {
return (
<div className="text-center text-lg mt-20">
<button
onClick={() => signIn()}
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
>
Sign in
</button>
<div>
<p>Please sign in to view your profile.</p>
<button onClick={() => signIn()}>Sign in</button>
</div>
);
}

return (
<div className="max-w-md mx-auto p-4 pt-6">
<h1 className="text-3xl mb-4">Profile</h1>
{/* Display user data */}
<div className="flex justify-between mb-4">
<span className="text-lg">Name:</span>
{nameEditMode ? (
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
/>
) : (
<span className="text-lg">{name}</span>
)}
{nameEditMode ? (
<button
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
onClick={handleNameChange}
>
Save
</button>
) : (
<button
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
onClick={() => setNameEditMode(true)}
>
Edit
</button>
)}
</div>
{/* Additional user details */}
<div className="flex justify-between mb-4">
<span className="text-lg">Email:</span>
<span className="text-lg">{user?.email}</span>
</div>
<div className="flex justify-between mb-4">
<span className="text-lg">Plan:</span>
<span className="text-lg">{userData?.plan}</span>
</div>
<div className="flex justify-between mb-4">
<span className="text-lg">Request Count:</span>
<span className="text-lg">{userData?.requestCount}</span>
</div>
<div className="flex justify-between mb-4">
<span className="text-lg">Usage:</span>
<span className="text-lg">{calculateUsage(userData?.requestCount ?? 0, userData?.plan ?? "free")}</span>
</div>
{/* Connected providers */}
<div className="flex justify-between mb-4">
<span className="text-lg">Connected Providers:</span>
<ul>
{connectedProviders.map((provider) => (
<li key={provider}>{provider.charAt(0).toUpperCase() + provider.slice(1)}</li>
<div className="profile-page">
<h1>Profile Page</h1>
<div className="profile-info">
<p><strong>Email:</strong> {user?.email}</p>
<p><strong>Plan:</strong> {userData?.plan}</p>
<p><strong>Request Count:</strong> {userData?.requestCount}</p>
<p><strong>Usage:</strong> {calculateUsage(userData?.requestCount || 0, userData?.plan || "free")}</p>
<div className="name-edit">
<strong>Name:</strong>
{nameEditMode ? (
<div>
<input type="text" value={name} onChange={(e) => setName(e.target.value)} placeholder="Enter new name" />
<button onClick={handleNameChange}>Save</button>
<button onClick={() => setNameEditMode(false)}>Cancel</button>
</div>
) : (
<div>
<span>{name || "Not set"}</span>
<button onClick={() => setNameEditMode(true)}>Edit</button>
</div>
)}
</div>
<div className="provider-links">
<h3>Connect Providers:</h3>
{availableProviders && Object.keys(availableProviders).map((providerId) => (
<button key={providerId} onClick={() => handleProviderLink(providerId)} disabled={connectedProviders.includes(providerId)}>
{connectedProviders.includes(providerId) ? `${providerId} (Connected)` : `Connect ${providerId}`}
</button>
))}
</ul>
</div>
<div className="flex justify-between mb-4">
<button
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
onClick={() => handleProviderLink("google")}
>
Link Google
</button>
<button
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
onClick={() => handleProviderLink("github")}
>
Link GitHub
</button>
</div>
</div>
<button
className="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded"
onClick={handleSignOut}
>
Sign Out
</button>
<button onClick={() => signOut(auth).catch((error) => console.error("Sign out error:", error))}>Sign Out</button>
</div>
);
};
Expand Down
2 changes: 1 addition & 1 deletion firebaseFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export const getUserData = async (email: string): Promise<UserData | null> => {
};

// Update user profile data in Firestore
export const updateUserProfile = async (email: string, updatedData: Partial<UserData>): Promise<void> => {
export const updateUserData = async (email: string, updatedData: Partial<UserData>): Promise<void> => {
try {
const userDocRef = doc(db, "users", email);
await updateDoc(userDocRef, updatedData);
Expand Down

0 comments on commit 8082c14

Please sign in to comment.