From 190243081a68623fb6855da8df24010e7a9c4245 Mon Sep 17 00:00:00 2001 From: Jeet Debnath Date: Tue, 24 Feb 2026 11:37:18 +0530 Subject: [PATCH] added firestore config --- src/contexts/AuthContext.tsx | 25 ++++++- src/lib/firebase.ts | 11 +++ src/lib/firestoreConfig.ts | 65 ++++++++++++++++++ src/lib/firestoreService.ts | 130 +++++++++++++++++++++++++++++++++++ 4 files changed, 229 insertions(+), 2 deletions(-) create mode 100644 src/lib/firestoreConfig.ts create mode 100644 src/lib/firestoreService.ts diff --git a/src/contexts/AuthContext.tsx b/src/contexts/AuthContext.tsx index d457942..4aafeb1 100644 --- a/src/contexts/AuthContext.tsx +++ b/src/contexts/AuthContext.tsx @@ -13,7 +13,9 @@ import { signOut as firebaseSignOut, type User, } from 'firebase/auth' -import { auth, googleProvider } from '../lib/firebase' +import { auth, googleProvider, db } from '../lib/firebase' +import { doc, setDoc } from 'firebase/firestore' +import { COLLECTIONS } from '../lib/firestoreConfig' type AuthContextValue = { user: User | null @@ -28,9 +30,28 @@ export function AuthProvider({ children }: { children: ReactNode }) { const [user, setUser] = useState(null) const [loading, setLoading] = useState(true) + // Save user info to Firestore when they authenticate + async function saveUserToFirestore(authUser: User) { + try { + const userRef = doc(db, COLLECTIONS.USERS, authUser.uid) + await setDoc(userRef, { + id: authUser.uid, + email: authUser.email || '', + displayName: authUser.displayName || '', + photoURL: authUser.photoURL || '', + lastLoginAt: Date.now(), + }, { merge: true }) + } catch (error) { + console.error('Error saving user to Firestore:', error) + } + } + useEffect(() => { - const unsubscribe = onAuthStateChanged(auth, (u) => { + const unsubscribe = onAuthStateChanged(auth, async (u) => { setUser(u) + if (u) { + await saveUserToFirestore(u) + } setLoading(false) }) return () => unsubscribe() diff --git a/src/lib/firebase.ts b/src/lib/firebase.ts index a3a42f0..7dcec13 100644 --- a/src/lib/firebase.ts +++ b/src/lib/firebase.ts @@ -1,5 +1,6 @@ import { initializeApp } from 'firebase/app' import { getAuth, GoogleAuthProvider } from 'firebase/auth' +import { getFirestore, connectFirestoreEmulator } from 'firebase/firestore' const firebaseConfig = { apiKey: import.meta.env.VITE_FIREBASE_API_KEY, @@ -12,5 +13,15 @@ const firebaseConfig = { } const app = initializeApp(firebaseConfig) + +// Auth initialization export const auth = getAuth(app) export const googleProvider = new GoogleAuthProvider() + +// Firestore initialization +export const db = getFirestore(app) + +// Enable Firestore emulator in development (uncomment when testing locally) +// if (import.meta.env.DEV) { +// connectFirestoreEmulator(db, 'localhost', 8080) +// } diff --git a/src/lib/firestoreConfig.ts b/src/lib/firestoreConfig.ts new file mode 100644 index 0000000..15bf19c --- /dev/null +++ b/src/lib/firestoreConfig.ts @@ -0,0 +1,65 @@ +/** + * Firestore Collections Configuration + * Define all collection names and common data structure interfaces here + */ + +// Collection names +export const COLLECTIONS = { + USERS: 'users', + ENTRIES: 'entries', + SETTINGS: 'settings', + TAGS: 'tags', +} as const + +// User document interface +export interface FirestoreUser { + id: string + email: string + displayName?: string + photoURL?: string + createdAt: number + updatedAt: number + theme?: 'light' | 'dark' +} + +// Entry (Journal Entry) document interface +export interface JournalEntry { + id: string + userId: string + title: string + content: string + mood?: 'happy' | 'sad' | 'neutral' | 'anxious' | 'grateful' + tags?: string[] + isPublic?: boolean + createdAt: number + updatedAt: number +} + +// Settings document interface +export interface UserSettings { + userId: string + notifications?: boolean + emailNotifications?: boolean + theme?: 'light' | 'dark' | 'system' + language?: string + updatedAt: number +} + +// Tags document interface +export interface Tag { + id: string + userId: string + name: string + color?: string + createdAt: number + updatedAt: number +} + +// Firestore emulator configuration +export function isEmulatorEnabled(): boolean { + return import.meta.env.VITE_FIREBASE_EMULATOR_ENABLED === 'true' +} + +export function getEmulatorHost(): string { + return import.meta.env.VITE_FIRESTORE_EMULATOR_HOST || 'localhost:8080' +} diff --git a/src/lib/firestoreService.ts b/src/lib/firestoreService.ts new file mode 100644 index 0000000..f41586e --- /dev/null +++ b/src/lib/firestoreService.ts @@ -0,0 +1,130 @@ +import { + collection, + doc, + getDoc, + getDocs, + setDoc, + updateDoc, + deleteDoc, + query, + where, + orderBy, + WriteBatch, + writeBatch, +} from 'firebase/firestore' +import { db } from './firebase' + +type FirestoreData = Record + +/** + * Generic function to add or update a document + */ +export async function setDocument( + collectionName: string, + docId: string, + data: T, + merge = true +): Promise { + try { + await setDoc(doc(db, collectionName, docId), data, { merge }) + } catch (error) { + console.error(`Error setting document in ${collectionName}:`, error) + throw error + } +} + +/** + * Generic function to get a single document + */ +export async function getDocument( + collectionName: string, + docId: string +): Promise { + try { + const docRef = doc(db, collectionName, docId) + const docSnapshot = await getDoc(docRef) + return (docSnapshot.exists() ? docSnapshot.data() : null) as T | null + } catch (error) { + console.error(`Error getting document from ${collectionName}:`, error) + throw error + } +} + +/** + * Generic function to get all documents from a collection + */ +export async function getDocuments( + collectionName: string +): Promise { + try { + const querySnapshot = await getDocs(collection(db, collectionName)) + return querySnapshot.docs.map((doc) => ({ id: doc.id, ...doc.data() } as T)) + } catch (error) { + console.error(`Error getting documents from ${collectionName}:`, error) + throw error + } +} + +/** + * Generic function to query documents with conditions + */ +export async function queryDocuments( + collectionName: string, + constraints: any[] +): Promise { + try { + const q = query(collection(db, collectionName), ...constraints) + const querySnapshot = await getDocs(q) + return querySnapshot.docs.map((doc) => ({ id: doc.id, ...doc.data() } as T)) + } catch (error) { + console.error(`Error querying ${collectionName}:`, error) + throw error + } +} + +/** + * Generic function to update a document + */ +export async function updateDocument>( + collectionName: string, + docId: string, + data: T +): Promise { + try { + await updateDoc(doc(db, collectionName, docId), data) + } catch (error) { + console.error(`Error updating document in ${collectionName}:`, error) + throw error + } +} + +/** + * Generic function to delete a document + */ +export async function deleteDocument( + collectionName: string, + docId: string +): Promise { + try { + await deleteDoc(doc(db, collectionName, docId)) + } catch (error) { + console.error(`Error deleting document from ${collectionName}:`, error) + throw error + } +} + +/** + * Batch write operations + */ +export function createWriteBatch(): WriteBatch { + return writeBatch(db) +} + +export async function commitBatch(batch: WriteBatch): Promise { + try { + await batch.commit() + } catch (error) { + console.error('Error committing batch write:', error) + throw error + } +}