@marcwelti/mw-core 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/context/index.js +6 -1
- package/dist/context/index.js.map +1 -1
- package/dist/context/index.mjs +6 -1
- package/dist/context/index.mjs.map +1 -1
- package/dist/firebase/index.d.mts +1 -0
- package/dist/firebase/index.d.ts +1 -0
- package/dist/firebase/index.js +6 -1
- package/dist/firebase/index.js.map +1 -1
- package/dist/firebase/index.mjs +6 -1
- package/dist/firebase/index.mjs.map +1 -1
- package/dist/hooks/index.js +6 -1
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/index.mjs +6 -1
- package/dist/hooks/index.mjs.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +6 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/context/index.js
CHANGED
|
@@ -59,7 +59,12 @@ function getFirebaseAuth() {
|
|
|
59
59
|
}
|
|
60
60
|
function getFirebaseFirestore() {
|
|
61
61
|
if (!_db) {
|
|
62
|
-
|
|
62
|
+
const databaseId = process.env.NEXT_PUBLIC_FIRESTORE_DATABASE_ID;
|
|
63
|
+
if (databaseId) {
|
|
64
|
+
_db = firestore.getFirestore(getFirebaseApp(), databaseId);
|
|
65
|
+
} else {
|
|
66
|
+
_db = firestore.getFirestore(getFirebaseApp());
|
|
67
|
+
}
|
|
63
68
|
}
|
|
64
69
|
return _db;
|
|
65
70
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/firebase/config.ts","../../src/firebase/auth.ts","../../src/firebase/firestore.ts","../../src/context/AuthContext.tsx","../../src/context/AdminRoute.tsx"],"names":["getApps","getApp","initializeApp","getAuth","getFirestore","getStorage","auth","signInWithEmailAndPassword","createUserWithEmailAndPassword","updateProfile","firebaseSignOut","sendPasswordResetEmail","sendEmailVerification","GoogleAuthProvider","signInWithPopup","onAuthStateChanged","db","doc","onSnapshot","createContext","useState","useEffect","useCallback","error","signOut","signInWithGoogle","resetPassword","useMemo","jsx","useContext","Fragment"],"mappings":"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","file":"index.js","sourcesContent":["import { initializeApp, getApps, getApp, FirebaseApp } from 'firebase/app';\nimport { getAuth, Auth } from 'firebase/auth';\nimport { getFirestore, Firestore } from 'firebase/firestore';\nimport { getStorage, FirebaseStorage } from 'firebase/storage';\n\n/**\n * Firebase configuration interface\n * All values should be provided via environment variables\n */\nexport interface FirebaseConfig {\n apiKey: string;\n authDomain: string;\n projectId: string;\n storageBucket: string;\n messagingSenderId: string;\n appId: string;\n measurementId?: string;\n}\n\n/**\n * Get Firebase configuration from environment variables\n * Works with Next.js NEXT_PUBLIC_ prefix\n */\nexport function getFirebaseConfig(): FirebaseConfig {\n const config: FirebaseConfig = {\n apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY || '',\n authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN || '',\n projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID || '',\n storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET || '',\n messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID || '',\n appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID || '',\n measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID,\n };\n\n // Validate required fields\n const requiredFields: (keyof FirebaseConfig)[] = [\n 'apiKey',\n 'authDomain',\n 'projectId',\n 'storageBucket',\n 'messagingSenderId',\n 'appId',\n ];\n\n const missingFields = requiredFields.filter((field) => !config[field]);\n\n if (missingFields.length > 0) {\n console.warn(\n `[mw-core] Missing Firebase config fields: ${missingFields.join(', ')}. ` +\n `Make sure to set NEXT_PUBLIC_FIREBASE_* environment variables.`\n );\n }\n\n return config;\n}\n\n/**\n * Initialize Firebase app (singleton pattern)\n * Safe to call multiple times - returns existing instance if already initialized\n */\nexport function initializeFirebase(config?: FirebaseConfig): FirebaseApp {\n if (getApps().length > 0) {\n return getApp();\n }\n\n const firebaseConfig = config || getFirebaseConfig();\n return initializeApp(firebaseConfig);\n}\n\n// Lazy initialization singletons\nlet _app: FirebaseApp | null = null;\nlet _auth: Auth | null = null;\nlet _db: Firestore | null = null;\nlet _storage: FirebaseStorage | null = null;\n\n/**\n * Get the Firebase app instance\n */\nexport function getFirebaseApp(): FirebaseApp {\n if (!_app) {\n _app = initializeFirebase();\n }\n return _app;\n}\n\n/**\n * Get the Firebase Auth instance\n */\nexport function getFirebaseAuth(): Auth {\n if (!_auth) {\n _auth = getAuth(getFirebaseApp());\n }\n return _auth;\n}\n\n/**\n * Get the Firestore database instance\n */\nexport function getFirebaseFirestore(): Firestore {\n if (!_db) {\n _db = getFirestore(getFirebaseApp());\n }\n return _db;\n}\n\n/**\n * Get the Firebase Storage instance\n */\nexport function getFirebaseStorage(): FirebaseStorage {\n if (!_storage) {\n _storage = getStorage(getFirebaseApp());\n }\n return _storage;\n}\n\n// Convenience exports for direct access\nexport const app = typeof window !== 'undefined' ? getFirebaseApp() : null;\nexport const auth = typeof window !== 'undefined' ? getFirebaseAuth() : null;\nexport const db = typeof window !== 'undefined' ? getFirebaseFirestore() : null;\nexport const storage = typeof window !== 'undefined' ? getFirebaseStorage() : null;\n\n","import {\n signInWithEmailAndPassword,\n createUserWithEmailAndPassword,\n signOut as firebaseSignOut,\n sendPasswordResetEmail,\n sendEmailVerification,\n updateProfile,\n GoogleAuthProvider,\n signInWithPopup,\n signInWithRedirect,\n getRedirectResult,\n onAuthStateChanged,\n User,\n UserCredential,\n Auth,\n} from 'firebase/auth';\nimport { getFirebaseAuth } from './config';\n\n/**\n * Sign in with email and password\n */\nexport async function signInWithEmail(\n email: string,\n password: string\n): Promise<UserCredential> {\n const auth = getFirebaseAuth();\n return signInWithEmailAndPassword(auth, email, password);\n}\n\n/**\n * Create a new account with email and password\n */\nexport async function signUpWithEmail(\n email: string,\n password: string,\n displayName?: string\n): Promise<UserCredential> {\n const auth = getFirebaseAuth();\n const credential = await createUserWithEmailAndPassword(auth, email, password);\n \n if (displayName && credential.user) {\n await updateProfile(credential.user, { displayName });\n }\n \n return credential;\n}\n\n/**\n * Sign out the current user\n */\nexport async function signOut(): Promise<void> {\n const auth = getFirebaseAuth();\n return firebaseSignOut(auth);\n}\n\n/**\n * Send a password reset email\n */\nexport async function resetPassword(email: string): Promise<void> {\n const auth = getFirebaseAuth();\n return sendPasswordResetEmail(auth, email);\n}\n\n/**\n * Send email verification to the current user\n */\nexport async function sendVerificationEmail(user: User): Promise<void> {\n return sendEmailVerification(user);\n}\n\n/**\n * Update the current user's profile\n */\nexport async function updateUserProfile(\n user: User,\n profile: { displayName?: string; photoURL?: string }\n): Promise<void> {\n return updateProfile(user, profile);\n}\n\n/**\n * Sign in with Google using popup\n */\nexport async function signInWithGoogle(): Promise<UserCredential> {\n const auth = getFirebaseAuth();\n const provider = new GoogleAuthProvider();\n provider.addScope('email');\n provider.addScope('profile');\n return signInWithPopup(auth, provider);\n}\n\n/**\n * Sign in with Google using redirect (better for mobile)\n */\nexport async function signInWithGoogleRedirect(): Promise<void> {\n const auth = getFirebaseAuth();\n const provider = new GoogleAuthProvider();\n provider.addScope('email');\n provider.addScope('profile');\n return signInWithRedirect(auth, provider);\n}\n\n/**\n * Get the result of a redirect sign-in\n */\nexport async function getGoogleRedirectResult(): Promise<UserCredential | null> {\n const auth = getFirebaseAuth();\n return getRedirectResult(auth);\n}\n\n/**\n * Subscribe to auth state changes\n */\nexport function subscribeToAuthState(\n callback: (user: User | null) => void\n): () => void {\n const auth = getFirebaseAuth();\n return onAuthStateChanged(auth, callback);\n}\n\n/**\n * Get the current user (may be null if not authenticated)\n */\nexport function getCurrentUser(): User | null {\n const auth = getFirebaseAuth();\n return auth.currentUser;\n}\n\n/**\n * Wait for the auth state to be determined\n * Useful for SSR/initial load\n */\nexport function waitForAuthState(): Promise<User | null> {\n return new Promise((resolve) => {\n const auth = getFirebaseAuth();\n const unsubscribe = onAuthStateChanged(auth, (user) => {\n unsubscribe();\n resolve(user);\n });\n });\n}\n\n// Re-export useful types\nexport type { User, UserCredential, Auth };\n\n","import {\n collection,\n doc,\n getDoc,\n getDocs,\n setDoc,\n updateDoc,\n deleteDoc,\n addDoc,\n query,\n where,\n orderBy,\n limit,\n startAfter,\n onSnapshot,\n DocumentData,\n DocumentReference,\n CollectionReference,\n QueryConstraint,\n DocumentSnapshot,\n QuerySnapshot,\n Timestamp,\n serverTimestamp,\n FieldValue,\n WhereFilterOp,\n OrderByDirection,\n Unsubscribe,\n} from 'firebase/firestore';\nimport { getFirebaseFirestore } from './config';\n\n/**\n * Get a document reference\n */\nexport function getDocRef<T = DocumentData>(\n collectionPath: string,\n documentId: string\n): DocumentReference<T> {\n const db = getFirebaseFirestore();\n return doc(db, collectionPath, documentId) as DocumentReference<T>;\n}\n\n/**\n * Get a collection reference\n */\nexport function getCollectionRef<T = DocumentData>(\n collectionPath: string\n): CollectionReference<T> {\n const db = getFirebaseFirestore();\n return collection(db, collectionPath) as CollectionReference<T>;\n}\n\n/**\n * Get a single document by ID\n */\nexport async function getDocument<T = DocumentData>(\n collectionPath: string,\n documentId: string\n): Promise<T | null> {\n const docRef = getDocRef<T>(collectionPath, documentId);\n const docSnap = await getDoc(docRef);\n \n if (docSnap.exists()) {\n return { id: docSnap.id, ...docSnap.data() } as T;\n }\n \n return null;\n}\n\n/**\n * Get all documents in a collection\n */\nexport async function getCollection<T = DocumentData>(\n collectionPath: string,\n constraints: QueryConstraint[] = []\n): Promise<T[]> {\n const collectionRef = getCollectionRef<T>(collectionPath);\n const q = query(collectionRef, ...constraints);\n const querySnapshot = await getDocs(q);\n \n return querySnapshot.docs.map((doc) => ({\n id: doc.id,\n ...doc.data(),\n })) as T[];\n}\n\n/**\n * Add a new document with auto-generated ID\n */\nexport async function addDocument<T = DocumentData>(\n collectionPath: string,\n data: Omit<T, 'id'>\n): Promise<string> {\n const collectionRef = getCollectionRef(collectionPath);\n const docRef = await addDoc(collectionRef, {\n ...data,\n createdAt: serverTimestamp(),\n updatedAt: serverTimestamp(),\n });\n return docRef.id;\n}\n\n/**\n * Set/create a document with a specific ID\n */\nexport async function setDocument<T = DocumentData>(\n collectionPath: string,\n documentId: string,\n data: Omit<T, 'id'>,\n merge: boolean = false\n): Promise<void> {\n const docRef = getDocRef(collectionPath, documentId);\n await setDoc(\n docRef,\n {\n ...data,\n updatedAt: serverTimestamp(),\n ...(merge ? {} : { createdAt: serverTimestamp() }),\n },\n { merge }\n );\n}\n\n/**\n * Update specific fields in a document\n */\nexport async function updateDocument<T = DocumentData>(\n collectionPath: string,\n documentId: string,\n data: Partial<T>\n): Promise<void> {\n const docRef = getDocRef(collectionPath, documentId);\n await updateDoc(docRef, {\n ...data,\n updatedAt: serverTimestamp(),\n });\n}\n\n/**\n * Delete a document\n */\nexport async function deleteDocument(\n collectionPath: string,\n documentId: string\n): Promise<void> {\n const docRef = getDocRef(collectionPath, documentId);\n await deleteDoc(docRef);\n}\n\n/**\n * Subscribe to a single document\n */\nexport function subscribeToDocument<T = DocumentData>(\n collectionPath: string,\n documentId: string,\n callback: (data: T | null) => void\n): Unsubscribe {\n const docRef = getDocRef<T>(collectionPath, documentId);\n \n return onSnapshot(docRef, (docSnap) => {\n if (docSnap.exists()) {\n callback({ id: docSnap.id, ...docSnap.data() } as T);\n } else {\n callback(null);\n }\n });\n}\n\n/**\n * Subscribe to a collection\n */\nexport function subscribeToCollection<T = DocumentData>(\n collectionPath: string,\n callback: (data: T[]) => void,\n constraints: QueryConstraint[] = []\n): Unsubscribe {\n const collectionRef = getCollectionRef<T>(collectionPath);\n const q = query(collectionRef, ...constraints);\n \n return onSnapshot(q, (querySnapshot) => {\n const data = querySnapshot.docs.map((doc) => ({\n id: doc.id,\n ...doc.data(),\n })) as T[];\n callback(data);\n });\n}\n\n// Export query helpers for convenience\nexport {\n query,\n where,\n orderBy,\n limit,\n startAfter,\n Timestamp,\n serverTimestamp,\n};\n\n// Re-export types\nexport type {\n DocumentData,\n DocumentReference,\n CollectionReference,\n QueryConstraint,\n DocumentSnapshot,\n QuerySnapshot,\n FieldValue,\n WhereFilterOp,\n OrderByDirection,\n Unsubscribe,\n};\n\n","'use client';\n\nimport React, {\n createContext,\n useContext,\n useState,\n useEffect,\n useCallback,\n useMemo,\n ReactNode,\n} from 'react';\nimport { User, UserCredential } from 'firebase/auth';\nimport {\n subscribeToAuthState,\n signInWithEmail,\n signUpWithEmail,\n signOut as firebaseSignOut,\n signInWithGoogle as firebaseSignInWithGoogle,\n resetPassword as firebaseResetPassword,\n updateUserProfile,\n sendVerificationEmail,\n} from '../firebase/auth';\nimport { subscribeToDocument } from '../firebase/firestore';\nimport type { UserRole } from '../types';\n\n/**\n * User data from Firestore (includes role and additional profile data)\n */\nexport interface UserData {\n id: string;\n uid: string;\n email: string | null;\n displayName: string | null;\n photoURL: string | null;\n role: UserRole | null;\n phoneNumber?: string | null;\n isActive?: boolean;\n}\n\nexport interface AuthContextValue {\n /** The current authenticated Firebase user, or null if not authenticated */\n user: User | null;\n /** User data from Firestore (includes role) */\n userData: UserData | null;\n /** The user's role from Firestore */\n role: UserRole | null;\n /** Whether the auth state is still loading */\n loading: boolean;\n /** Whether the user data is still loading from Firestore */\n userDataLoading: boolean;\n /** Whether the user is authenticated */\n isAuthenticated: boolean;\n /** Whether the user has admin access (owner or admin role) */\n isAdmin: boolean;\n /** Whether the user is staff (owner, admin, or employee role) */\n isStaff: boolean;\n /** Whether the user is the owner */\n isOwner: boolean;\n /** Any auth error that occurred */\n error: Error | null;\n /** Sign in with email and password */\n signIn: (email: string, password: string) => Promise<UserCredential>;\n /** Create a new account with email and password */\n signUp: (email: string, password: string, displayName?: string) => Promise<UserCredential>;\n /** Sign out the current user */\n signOut: () => Promise<void>;\n /** Sign in with Google */\n signInWithGoogle: () => Promise<UserCredential>;\n /** Send a password reset email */\n resetPassword: (email: string) => Promise<void>;\n /** Update the user's profile */\n updateProfile: (profile: { displayName?: string; photoURL?: string }) => Promise<void>;\n /** Send email verification */\n sendEmailVerification: () => Promise<void>;\n /** Clear any auth errors */\n clearError: () => void;\n /** Refresh user data from Firestore */\n refreshUserData: () => void;\n}\n\nconst AuthContext = createContext<AuthContextValue | undefined>(undefined);\n\nexport interface AuthProviderProps {\n children: ReactNode;\n /** Optional: Called when auth state changes */\n onAuthStateChange?: (user: User | null) => void;\n /** Optional: Called when user data changes */\n onUserDataChange?: (userData: UserData | null) => void;\n /** Optional: Loading component to show while auth state is loading */\n loadingComponent?: ReactNode;\n /** Optional: Collection path for user documents (default: 'users') */\n usersCollection?: string;\n}\n\n/**\n * Auth Provider component that wraps your app and provides auth context\n * Automatically fetches user data from Firestore including role\n * \n * @example\n * ```tsx\n * // app/layout.tsx\n * import { AuthProvider } from '@marcwelti/mw-core';\n * \n * export default function RootLayout({ children }) {\n * return (\n * <html>\n * <body>\n * <AuthProvider>\n * {children}\n * </AuthProvider>\n * </body>\n * </html>\n * );\n * }\n * ```\n */\nexport function AuthProvider({\n children,\n onAuthStateChange,\n onUserDataChange,\n loadingComponent,\n usersCollection = 'users',\n}: AuthProviderProps) {\n const [user, setUser] = useState<User | null>(null);\n const [userData, setUserData] = useState<UserData | null>(null);\n const [loading, setLoading] = useState(true);\n const [userDataLoading, setUserDataLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [refreshTrigger, setRefreshTrigger] = useState(0);\n\n // Subscribe to Firebase Auth state\n useEffect(() => {\n const unsubscribe = subscribeToAuthState((firebaseUser) => {\n setUser(firebaseUser);\n setLoading(false);\n onAuthStateChange?.(firebaseUser);\n \n // Clear user data when logged out\n if (!firebaseUser) {\n setUserData(null);\n onUserDataChange?.(null);\n }\n });\n\n return unsubscribe;\n }, [onAuthStateChange, onUserDataChange]);\n\n // Subscribe to Firestore user document when authenticated\n useEffect(() => {\n if (!user) {\n setUserData(null);\n setUserDataLoading(false);\n return;\n }\n\n setUserDataLoading(true);\n\n const unsubscribe = subscribeToDocument<UserData>(\n usersCollection,\n user.uid,\n (data) => {\n if (data) {\n const mergedData: UserData = {\n id: user.uid,\n uid: user.uid,\n email: user.email,\n displayName: data.displayName || user.displayName,\n photoURL: data.photoURL || user.photoURL,\n role: data.role || null,\n phoneNumber: data.phoneNumber || user.phoneNumber,\n isActive: data.isActive,\n };\n setUserData(mergedData);\n onUserDataChange?.(mergedData);\n } else {\n // User document doesn't exist - create basic data without role\n const basicData: UserData = {\n id: user.uid,\n uid: user.uid,\n email: user.email,\n displayName: user.displayName,\n photoURL: user.photoURL,\n role: null,\n phoneNumber: user.phoneNumber,\n };\n setUserData(basicData);\n onUserDataChange?.(basicData);\n }\n setUserDataLoading(false);\n }\n );\n\n return unsubscribe;\n }, [user, usersCollection, onUserDataChange, refreshTrigger]);\n\n // Refresh user data function\n const refreshUserData = useCallback(() => {\n setRefreshTrigger((prev) => prev + 1);\n }, []);\n\n const signIn = useCallback(async (email: string, password: string) => {\n setError(null);\n try {\n return await signInWithEmail(email, password);\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n }, []);\n\n const signUp = useCallback(\n async (email: string, password: string, displayName?: string) => {\n setError(null);\n try {\n return await signUpWithEmail(email, password, displayName);\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n },\n []\n );\n\n const signOut = useCallback(async () => {\n setError(null);\n try {\n await firebaseSignOut();\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n }, []);\n\n const signInWithGoogle = useCallback(async () => {\n setError(null);\n try {\n return await firebaseSignInWithGoogle();\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n }, []);\n\n const resetPassword = useCallback(async (email: string) => {\n setError(null);\n try {\n await firebaseResetPassword(email);\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n }, []);\n\n const updateProfile = useCallback(\n async (profile: { displayName?: string; photoURL?: string }) => {\n setError(null);\n if (!user) {\n const error = new Error('No authenticated user');\n setError(error);\n throw error;\n }\n try {\n await updateUserProfile(user, profile);\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n },\n [user]\n );\n\n const sendEmailVerificationFn = useCallback(async () => {\n setError(null);\n if (!user) {\n const error = new Error('No authenticated user');\n setError(error);\n throw error;\n }\n try {\n await sendVerificationEmail(user);\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n }, [user]);\n\n const clearError = useCallback(() => {\n setError(null);\n }, []);\n\n // Computed role-based properties\n const role = userData?.role ?? null;\n const isAdmin = role === 'owner' || role === 'admin';\n const isStaff = role === 'owner' || role === 'admin' || role === 'employee';\n const isOwner = role === 'owner';\n\n const value = useMemo<AuthContextValue>(\n () => ({\n user,\n userData,\n role,\n loading,\n userDataLoading,\n isAuthenticated: !!user,\n isAdmin,\n isStaff,\n isOwner,\n error,\n signIn,\n signUp,\n signOut,\n signInWithGoogle,\n resetPassword,\n updateProfile,\n sendEmailVerification: sendEmailVerificationFn,\n clearError,\n refreshUserData,\n }),\n [\n user,\n userData,\n role,\n loading,\n userDataLoading,\n isAdmin,\n isStaff,\n isOwner,\n error,\n signIn,\n signUp,\n signOut,\n signInWithGoogle,\n resetPassword,\n updateProfile,\n sendEmailVerificationFn,\n clearError,\n refreshUserData,\n ]\n );\n\n // Show loading component while determining auth state\n if (loading && loadingComponent) {\n return <>{loadingComponent}</>;\n }\n\n return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;\n}\n\n/**\n * Hook to access auth context\n * Must be used within an AuthProvider\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { user, signOut, isAuthenticated } = useAuthContext();\n * \n * if (!isAuthenticated) {\n * return <LoginForm />;\n * }\n * \n * return (\n * <div>\n * <p>Welcome, {user?.displayName}</p>\n * <button onClick={signOut}>Sign Out</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useAuthContext(): AuthContextValue {\n const context = useContext(AuthContext);\n \n if (context === undefined) {\n throw new Error('useAuthContext must be used within an AuthProvider');\n }\n \n return context;\n}\n\n/**\n * HOC for protecting routes that require authentication\n * Redirects to login or shows loading state\n */\nexport function withAuth<P extends object>(\n WrappedComponent: React.ComponentType<P>,\n options: {\n LoadingComponent?: React.ComponentType;\n UnauthenticatedComponent?: React.ComponentType;\n } = {}\n) {\n const { LoadingComponent, UnauthenticatedComponent } = options;\n\n return function WithAuthComponent(props: P) {\n const { user, loading, isAuthenticated } = useAuthContext();\n\n if (loading) {\n return LoadingComponent ? <LoadingComponent /> : <div>Loading...</div>;\n }\n\n if (!isAuthenticated) {\n return UnauthenticatedComponent ? (\n <UnauthenticatedComponent />\n ) : (\n <div>Please sign in to continue</div>\n );\n }\n\n return <WrappedComponent {...props} />;\n };\n}\n\n","'use client';\n\nimport React, { useEffect, ReactNode } from 'react';\nimport { useAuthContext } from './AuthContext';\n\nexport interface AdminRouteProps {\n children: ReactNode;\n /** URL to redirect non-admin users to (default: '/') */\n redirectTo?: string;\n /** Custom loading component */\n loadingComponent?: ReactNode;\n /** Custom access denied component (shown briefly before redirect) */\n accessDeniedComponent?: ReactNode;\n /** If true, also allows 'employee' role (uses isStaff instead of isAdmin) */\n allowStaff?: boolean;\n /** Callback when access is denied */\n onAccessDenied?: () => void;\n}\n\n/**\n * Wrapper component that protects routes for admin-only access\n * Redirects non-admin users to the specified path\n * \n * @example\n * ```tsx\n * // app/admin/page.tsx\n * import { AdminRoute } from '@marcwelti/mw-core';\n * \n * export default function AdminPage() {\n * return (\n * <AdminRoute redirectTo=\"/login\">\n * <AdminDashboard />\n * </AdminRoute>\n * );\n * }\n * ```\n */\nexport function AdminRoute({\n children,\n redirectTo = '/',\n loadingComponent,\n accessDeniedComponent,\n allowStaff = false,\n onAccessDenied,\n}: AdminRouteProps) {\n const { user, loading, userDataLoading, isAdmin, isStaff } = useAuthContext();\n\n const hasAccess = allowStaff ? isStaff : isAdmin;\n const isLoading = loading || userDataLoading;\n\n useEffect(() => {\n // Only redirect after loading is complete\n if (isLoading) return;\n\n // Not authenticated or no access\n if (!user || !hasAccess) {\n onAccessDenied?.();\n \n // Use window.location for redirect to work in all environments\n if (typeof window !== 'undefined') {\n window.location.href = redirectTo;\n }\n }\n }, [user, isLoading, hasAccess, redirectTo, onAccessDenied]);\n\n // Show loading state\n if (isLoading) {\n return loadingComponent ? (\n <>{loadingComponent}</>\n ) : (\n <div className=\"flex min-h-screen items-center justify-center\">\n <div className=\"h-8 w-8 animate-spin rounded-full border-4 border-primary border-t-transparent\" />\n </div>\n );\n }\n\n // Not authenticated or no access - show nothing or access denied component while redirecting\n if (!user || !hasAccess) {\n return accessDeniedComponent ? <>{accessDeniedComponent}</> : null;\n }\n\n // User has access\n return <>{children}</>;\n}\n\n/**\n * Wrapper component that protects routes for staff-only access (owner, admin, or employee)\n * Convenience wrapper around AdminRoute with allowStaff=true\n */\nexport function StaffRoute(props: Omit<AdminRouteProps, 'allowStaff'>) {\n return <AdminRoute {...props} allowStaff />;\n}\n\n/**\n * Wrapper component that protects routes for owner-only access\n */\nexport function OwnerRoute({\n children,\n redirectTo = '/',\n loadingComponent,\n accessDeniedComponent,\n onAccessDenied,\n}: Omit<AdminRouteProps, 'allowStaff'>) {\n const { user, loading, userDataLoading, isOwner } = useAuthContext();\n\n const isLoading = loading || userDataLoading;\n\n useEffect(() => {\n if (isLoading) return;\n\n if (!user || !isOwner) {\n onAccessDenied?.();\n \n if (typeof window !== 'undefined') {\n window.location.href = redirectTo;\n }\n }\n }, [user, isLoading, isOwner, redirectTo, onAccessDenied]);\n\n if (isLoading) {\n return loadingComponent ? (\n <>{loadingComponent}</>\n ) : (\n <div className=\"flex min-h-screen items-center justify-center\">\n <div className=\"h-8 w-8 animate-spin rounded-full border-4 border-primary border-t-transparent\" />\n </div>\n );\n }\n\n if (!user || !isOwner) {\n return accessDeniedComponent ? <>{accessDeniedComponent}</> : null;\n }\n\n return <>{children}</>;\n}\n\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/firebase/config.ts","../../src/firebase/auth.ts","../../src/firebase/firestore.ts","../../src/context/AuthContext.tsx","../../src/context/AdminRoute.tsx"],"names":["getApps","getApp","initializeApp","getAuth","getFirestore","getStorage","auth","signInWithEmailAndPassword","createUserWithEmailAndPassword","updateProfile","firebaseSignOut","sendPasswordResetEmail","sendEmailVerification","GoogleAuthProvider","signInWithPopup","onAuthStateChanged","db","doc","onSnapshot","createContext","useState","useEffect","useCallback","error","signOut","signInWithGoogle","resetPassword","useMemo","jsx","useContext","Fragment"],"mappings":"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","file":"index.js","sourcesContent":["import { initializeApp, getApps, getApp, FirebaseApp } from 'firebase/app';\nimport { getAuth, Auth } from 'firebase/auth';\nimport { getFirestore, Firestore } from 'firebase/firestore';\nimport { getStorage, FirebaseStorage } from 'firebase/storage';\n\n/**\n * Firebase configuration interface\n * All values should be provided via environment variables\n */\nexport interface FirebaseConfig {\n apiKey: string;\n authDomain: string;\n projectId: string;\n storageBucket: string;\n messagingSenderId: string;\n appId: string;\n measurementId?: string;\n}\n\n/**\n * Get Firebase configuration from environment variables\n * Works with Next.js NEXT_PUBLIC_ prefix\n */\nexport function getFirebaseConfig(): FirebaseConfig {\n const config: FirebaseConfig = {\n apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY || '',\n authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN || '',\n projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID || '',\n storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET || '',\n messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID || '',\n appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID || '',\n measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID,\n };\n\n // Validate required fields\n const requiredFields: (keyof FirebaseConfig)[] = [\n 'apiKey',\n 'authDomain',\n 'projectId',\n 'storageBucket',\n 'messagingSenderId',\n 'appId',\n ];\n\n const missingFields = requiredFields.filter((field) => !config[field]);\n\n if (missingFields.length > 0) {\n console.warn(\n `[mw-core] Missing Firebase config fields: ${missingFields.join(', ')}. ` +\n `Make sure to set NEXT_PUBLIC_FIREBASE_* environment variables.`\n );\n }\n\n return config;\n}\n\n/**\n * Initialize Firebase app (singleton pattern)\n * Safe to call multiple times - returns existing instance if already initialized\n */\nexport function initializeFirebase(config?: FirebaseConfig): FirebaseApp {\n if (getApps().length > 0) {\n return getApp();\n }\n\n const firebaseConfig = config || getFirebaseConfig();\n return initializeApp(firebaseConfig);\n}\n\n// Lazy initialization singletons\nlet _app: FirebaseApp | null = null;\nlet _auth: Auth | null = null;\nlet _db: Firestore | null = null;\nlet _storage: FirebaseStorage | null = null;\n\n/**\n * Get the Firebase app instance\n */\nexport function getFirebaseApp(): FirebaseApp {\n if (!_app) {\n _app = initializeFirebase();\n }\n return _app;\n}\n\n/**\n * Get the Firebase Auth instance\n */\nexport function getFirebaseAuth(): Auth {\n if (!_auth) {\n _auth = getAuth(getFirebaseApp());\n }\n return _auth;\n}\n\n/**\n * Get the Firestore database instance\n * Supports named databases via NEXT_PUBLIC_FIRESTORE_DATABASE_ID env var\n */\nexport function getFirebaseFirestore(): Firestore {\n if (!_db) {\n const databaseId = process.env.NEXT_PUBLIC_FIRESTORE_DATABASE_ID;\n if (databaseId) {\n _db = getFirestore(getFirebaseApp(), databaseId);\n } else {\n _db = getFirestore(getFirebaseApp());\n }\n }\n return _db;\n}\n\n/**\n * Get the Firebase Storage instance\n */\nexport function getFirebaseStorage(): FirebaseStorage {\n if (!_storage) {\n _storage = getStorage(getFirebaseApp());\n }\n return _storage;\n}\n\n// Convenience exports for direct access\nexport const app = typeof window !== 'undefined' ? getFirebaseApp() : null;\nexport const auth = typeof window !== 'undefined' ? getFirebaseAuth() : null;\nexport const db = typeof window !== 'undefined' ? getFirebaseFirestore() : null;\nexport const storage = typeof window !== 'undefined' ? getFirebaseStorage() : null;\n\n","import {\n signInWithEmailAndPassword,\n createUserWithEmailAndPassword,\n signOut as firebaseSignOut,\n sendPasswordResetEmail,\n sendEmailVerification,\n updateProfile,\n GoogleAuthProvider,\n signInWithPopup,\n signInWithRedirect,\n getRedirectResult,\n onAuthStateChanged,\n User,\n UserCredential,\n Auth,\n} from 'firebase/auth';\nimport { getFirebaseAuth } from './config';\n\n/**\n * Sign in with email and password\n */\nexport async function signInWithEmail(\n email: string,\n password: string\n): Promise<UserCredential> {\n const auth = getFirebaseAuth();\n return signInWithEmailAndPassword(auth, email, password);\n}\n\n/**\n * Create a new account with email and password\n */\nexport async function signUpWithEmail(\n email: string,\n password: string,\n displayName?: string\n): Promise<UserCredential> {\n const auth = getFirebaseAuth();\n const credential = await createUserWithEmailAndPassword(auth, email, password);\n \n if (displayName && credential.user) {\n await updateProfile(credential.user, { displayName });\n }\n \n return credential;\n}\n\n/**\n * Sign out the current user\n */\nexport async function signOut(): Promise<void> {\n const auth = getFirebaseAuth();\n return firebaseSignOut(auth);\n}\n\n/**\n * Send a password reset email\n */\nexport async function resetPassword(email: string): Promise<void> {\n const auth = getFirebaseAuth();\n return sendPasswordResetEmail(auth, email);\n}\n\n/**\n * Send email verification to the current user\n */\nexport async function sendVerificationEmail(user: User): Promise<void> {\n return sendEmailVerification(user);\n}\n\n/**\n * Update the current user's profile\n */\nexport async function updateUserProfile(\n user: User,\n profile: { displayName?: string; photoURL?: string }\n): Promise<void> {\n return updateProfile(user, profile);\n}\n\n/**\n * Sign in with Google using popup\n */\nexport async function signInWithGoogle(): Promise<UserCredential> {\n const auth = getFirebaseAuth();\n const provider = new GoogleAuthProvider();\n provider.addScope('email');\n provider.addScope('profile');\n return signInWithPopup(auth, provider);\n}\n\n/**\n * Sign in with Google using redirect (better for mobile)\n */\nexport async function signInWithGoogleRedirect(): Promise<void> {\n const auth = getFirebaseAuth();\n const provider = new GoogleAuthProvider();\n provider.addScope('email');\n provider.addScope('profile');\n return signInWithRedirect(auth, provider);\n}\n\n/**\n * Get the result of a redirect sign-in\n */\nexport async function getGoogleRedirectResult(): Promise<UserCredential | null> {\n const auth = getFirebaseAuth();\n return getRedirectResult(auth);\n}\n\n/**\n * Subscribe to auth state changes\n */\nexport function subscribeToAuthState(\n callback: (user: User | null) => void\n): () => void {\n const auth = getFirebaseAuth();\n return onAuthStateChanged(auth, callback);\n}\n\n/**\n * Get the current user (may be null if not authenticated)\n */\nexport function getCurrentUser(): User | null {\n const auth = getFirebaseAuth();\n return auth.currentUser;\n}\n\n/**\n * Wait for the auth state to be determined\n * Useful for SSR/initial load\n */\nexport function waitForAuthState(): Promise<User | null> {\n return new Promise((resolve) => {\n const auth = getFirebaseAuth();\n const unsubscribe = onAuthStateChanged(auth, (user) => {\n unsubscribe();\n resolve(user);\n });\n });\n}\n\n// Re-export useful types\nexport type { User, UserCredential, Auth };\n\n","import {\n collection,\n doc,\n getDoc,\n getDocs,\n setDoc,\n updateDoc,\n deleteDoc,\n addDoc,\n query,\n where,\n orderBy,\n limit,\n startAfter,\n onSnapshot,\n DocumentData,\n DocumentReference,\n CollectionReference,\n QueryConstraint,\n DocumentSnapshot,\n QuerySnapshot,\n Timestamp,\n serverTimestamp,\n FieldValue,\n WhereFilterOp,\n OrderByDirection,\n Unsubscribe,\n} from 'firebase/firestore';\nimport { getFirebaseFirestore } from './config';\n\n/**\n * Get a document reference\n */\nexport function getDocRef<T = DocumentData>(\n collectionPath: string,\n documentId: string\n): DocumentReference<T> {\n const db = getFirebaseFirestore();\n return doc(db, collectionPath, documentId) as DocumentReference<T>;\n}\n\n/**\n * Get a collection reference\n */\nexport function getCollectionRef<T = DocumentData>(\n collectionPath: string\n): CollectionReference<T> {\n const db = getFirebaseFirestore();\n return collection(db, collectionPath) as CollectionReference<T>;\n}\n\n/**\n * Get a single document by ID\n */\nexport async function getDocument<T = DocumentData>(\n collectionPath: string,\n documentId: string\n): Promise<T | null> {\n const docRef = getDocRef<T>(collectionPath, documentId);\n const docSnap = await getDoc(docRef);\n \n if (docSnap.exists()) {\n return { id: docSnap.id, ...docSnap.data() } as T;\n }\n \n return null;\n}\n\n/**\n * Get all documents in a collection\n */\nexport async function getCollection<T = DocumentData>(\n collectionPath: string,\n constraints: QueryConstraint[] = []\n): Promise<T[]> {\n const collectionRef = getCollectionRef<T>(collectionPath);\n const q = query(collectionRef, ...constraints);\n const querySnapshot = await getDocs(q);\n \n return querySnapshot.docs.map((doc) => ({\n id: doc.id,\n ...doc.data(),\n })) as T[];\n}\n\n/**\n * Add a new document with auto-generated ID\n */\nexport async function addDocument<T = DocumentData>(\n collectionPath: string,\n data: Omit<T, 'id'>\n): Promise<string> {\n const collectionRef = getCollectionRef(collectionPath);\n const docRef = await addDoc(collectionRef, {\n ...data,\n createdAt: serverTimestamp(),\n updatedAt: serverTimestamp(),\n });\n return docRef.id;\n}\n\n/**\n * Set/create a document with a specific ID\n */\nexport async function setDocument<T = DocumentData>(\n collectionPath: string,\n documentId: string,\n data: Omit<T, 'id'>,\n merge: boolean = false\n): Promise<void> {\n const docRef = getDocRef(collectionPath, documentId);\n await setDoc(\n docRef,\n {\n ...data,\n updatedAt: serverTimestamp(),\n ...(merge ? {} : { createdAt: serverTimestamp() }),\n },\n { merge }\n );\n}\n\n/**\n * Update specific fields in a document\n */\nexport async function updateDocument<T = DocumentData>(\n collectionPath: string,\n documentId: string,\n data: Partial<T>\n): Promise<void> {\n const docRef = getDocRef(collectionPath, documentId);\n await updateDoc(docRef, {\n ...data,\n updatedAt: serverTimestamp(),\n });\n}\n\n/**\n * Delete a document\n */\nexport async function deleteDocument(\n collectionPath: string,\n documentId: string\n): Promise<void> {\n const docRef = getDocRef(collectionPath, documentId);\n await deleteDoc(docRef);\n}\n\n/**\n * Subscribe to a single document\n */\nexport function subscribeToDocument<T = DocumentData>(\n collectionPath: string,\n documentId: string,\n callback: (data: T | null) => void\n): Unsubscribe {\n const docRef = getDocRef<T>(collectionPath, documentId);\n \n return onSnapshot(docRef, (docSnap) => {\n if (docSnap.exists()) {\n callback({ id: docSnap.id, ...docSnap.data() } as T);\n } else {\n callback(null);\n }\n });\n}\n\n/**\n * Subscribe to a collection\n */\nexport function subscribeToCollection<T = DocumentData>(\n collectionPath: string,\n callback: (data: T[]) => void,\n constraints: QueryConstraint[] = []\n): Unsubscribe {\n const collectionRef = getCollectionRef<T>(collectionPath);\n const q = query(collectionRef, ...constraints);\n \n return onSnapshot(q, (querySnapshot) => {\n const data = querySnapshot.docs.map((doc) => ({\n id: doc.id,\n ...doc.data(),\n })) as T[];\n callback(data);\n });\n}\n\n// Export query helpers for convenience\nexport {\n query,\n where,\n orderBy,\n limit,\n startAfter,\n Timestamp,\n serverTimestamp,\n};\n\n// Re-export types\nexport type {\n DocumentData,\n DocumentReference,\n CollectionReference,\n QueryConstraint,\n DocumentSnapshot,\n QuerySnapshot,\n FieldValue,\n WhereFilterOp,\n OrderByDirection,\n Unsubscribe,\n};\n\n","'use client';\n\nimport React, {\n createContext,\n useContext,\n useState,\n useEffect,\n useCallback,\n useMemo,\n ReactNode,\n} from 'react';\nimport { User, UserCredential } from 'firebase/auth';\nimport {\n subscribeToAuthState,\n signInWithEmail,\n signUpWithEmail,\n signOut as firebaseSignOut,\n signInWithGoogle as firebaseSignInWithGoogle,\n resetPassword as firebaseResetPassword,\n updateUserProfile,\n sendVerificationEmail,\n} from '../firebase/auth';\nimport { subscribeToDocument } from '../firebase/firestore';\nimport type { UserRole } from '../types';\n\n/**\n * User data from Firestore (includes role and additional profile data)\n */\nexport interface UserData {\n id: string;\n uid: string;\n email: string | null;\n displayName: string | null;\n photoURL: string | null;\n role: UserRole | null;\n phoneNumber?: string | null;\n isActive?: boolean;\n}\n\nexport interface AuthContextValue {\n /** The current authenticated Firebase user, or null if not authenticated */\n user: User | null;\n /** User data from Firestore (includes role) */\n userData: UserData | null;\n /** The user's role from Firestore */\n role: UserRole | null;\n /** Whether the auth state is still loading */\n loading: boolean;\n /** Whether the user data is still loading from Firestore */\n userDataLoading: boolean;\n /** Whether the user is authenticated */\n isAuthenticated: boolean;\n /** Whether the user has admin access (owner or admin role) */\n isAdmin: boolean;\n /** Whether the user is staff (owner, admin, or employee role) */\n isStaff: boolean;\n /** Whether the user is the owner */\n isOwner: boolean;\n /** Any auth error that occurred */\n error: Error | null;\n /** Sign in with email and password */\n signIn: (email: string, password: string) => Promise<UserCredential>;\n /** Create a new account with email and password */\n signUp: (email: string, password: string, displayName?: string) => Promise<UserCredential>;\n /** Sign out the current user */\n signOut: () => Promise<void>;\n /** Sign in with Google */\n signInWithGoogle: () => Promise<UserCredential>;\n /** Send a password reset email */\n resetPassword: (email: string) => Promise<void>;\n /** Update the user's profile */\n updateProfile: (profile: { displayName?: string; photoURL?: string }) => Promise<void>;\n /** Send email verification */\n sendEmailVerification: () => Promise<void>;\n /** Clear any auth errors */\n clearError: () => void;\n /** Refresh user data from Firestore */\n refreshUserData: () => void;\n}\n\nconst AuthContext = createContext<AuthContextValue | undefined>(undefined);\n\nexport interface AuthProviderProps {\n children: ReactNode;\n /** Optional: Called when auth state changes */\n onAuthStateChange?: (user: User | null) => void;\n /** Optional: Called when user data changes */\n onUserDataChange?: (userData: UserData | null) => void;\n /** Optional: Loading component to show while auth state is loading */\n loadingComponent?: ReactNode;\n /** Optional: Collection path for user documents (default: 'users') */\n usersCollection?: string;\n}\n\n/**\n * Auth Provider component that wraps your app and provides auth context\n * Automatically fetches user data from Firestore including role\n * \n * @example\n * ```tsx\n * // app/layout.tsx\n * import { AuthProvider } from '@marcwelti/mw-core';\n * \n * export default function RootLayout({ children }) {\n * return (\n * <html>\n * <body>\n * <AuthProvider>\n * {children}\n * </AuthProvider>\n * </body>\n * </html>\n * );\n * }\n * ```\n */\nexport function AuthProvider({\n children,\n onAuthStateChange,\n onUserDataChange,\n loadingComponent,\n usersCollection = 'users',\n}: AuthProviderProps) {\n const [user, setUser] = useState<User | null>(null);\n const [userData, setUserData] = useState<UserData | null>(null);\n const [loading, setLoading] = useState(true);\n const [userDataLoading, setUserDataLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [refreshTrigger, setRefreshTrigger] = useState(0);\n\n // Subscribe to Firebase Auth state\n useEffect(() => {\n const unsubscribe = subscribeToAuthState((firebaseUser) => {\n setUser(firebaseUser);\n setLoading(false);\n onAuthStateChange?.(firebaseUser);\n \n // Clear user data when logged out\n if (!firebaseUser) {\n setUserData(null);\n onUserDataChange?.(null);\n }\n });\n\n return unsubscribe;\n }, [onAuthStateChange, onUserDataChange]);\n\n // Subscribe to Firestore user document when authenticated\n useEffect(() => {\n if (!user) {\n setUserData(null);\n setUserDataLoading(false);\n return;\n }\n\n setUserDataLoading(true);\n\n const unsubscribe = subscribeToDocument<UserData>(\n usersCollection,\n user.uid,\n (data) => {\n if (data) {\n const mergedData: UserData = {\n id: user.uid,\n uid: user.uid,\n email: user.email,\n displayName: data.displayName || user.displayName,\n photoURL: data.photoURL || user.photoURL,\n role: data.role || null,\n phoneNumber: data.phoneNumber || user.phoneNumber,\n isActive: data.isActive,\n };\n setUserData(mergedData);\n onUserDataChange?.(mergedData);\n } else {\n // User document doesn't exist - create basic data without role\n const basicData: UserData = {\n id: user.uid,\n uid: user.uid,\n email: user.email,\n displayName: user.displayName,\n photoURL: user.photoURL,\n role: null,\n phoneNumber: user.phoneNumber,\n };\n setUserData(basicData);\n onUserDataChange?.(basicData);\n }\n setUserDataLoading(false);\n }\n );\n\n return unsubscribe;\n }, [user, usersCollection, onUserDataChange, refreshTrigger]);\n\n // Refresh user data function\n const refreshUserData = useCallback(() => {\n setRefreshTrigger((prev) => prev + 1);\n }, []);\n\n const signIn = useCallback(async (email: string, password: string) => {\n setError(null);\n try {\n return await signInWithEmail(email, password);\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n }, []);\n\n const signUp = useCallback(\n async (email: string, password: string, displayName?: string) => {\n setError(null);\n try {\n return await signUpWithEmail(email, password, displayName);\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n },\n []\n );\n\n const signOut = useCallback(async () => {\n setError(null);\n try {\n await firebaseSignOut();\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n }, []);\n\n const signInWithGoogle = useCallback(async () => {\n setError(null);\n try {\n return await firebaseSignInWithGoogle();\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n }, []);\n\n const resetPassword = useCallback(async (email: string) => {\n setError(null);\n try {\n await firebaseResetPassword(email);\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n }, []);\n\n const updateProfile = useCallback(\n async (profile: { displayName?: string; photoURL?: string }) => {\n setError(null);\n if (!user) {\n const error = new Error('No authenticated user');\n setError(error);\n throw error;\n }\n try {\n await updateUserProfile(user, profile);\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n },\n [user]\n );\n\n const sendEmailVerificationFn = useCallback(async () => {\n setError(null);\n if (!user) {\n const error = new Error('No authenticated user');\n setError(error);\n throw error;\n }\n try {\n await sendVerificationEmail(user);\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n }, [user]);\n\n const clearError = useCallback(() => {\n setError(null);\n }, []);\n\n // Computed role-based properties\n const role = userData?.role ?? null;\n const isAdmin = role === 'owner' || role === 'admin';\n const isStaff = role === 'owner' || role === 'admin' || role === 'employee';\n const isOwner = role === 'owner';\n\n const value = useMemo<AuthContextValue>(\n () => ({\n user,\n userData,\n role,\n loading,\n userDataLoading,\n isAuthenticated: !!user,\n isAdmin,\n isStaff,\n isOwner,\n error,\n signIn,\n signUp,\n signOut,\n signInWithGoogle,\n resetPassword,\n updateProfile,\n sendEmailVerification: sendEmailVerificationFn,\n clearError,\n refreshUserData,\n }),\n [\n user,\n userData,\n role,\n loading,\n userDataLoading,\n isAdmin,\n isStaff,\n isOwner,\n error,\n signIn,\n signUp,\n signOut,\n signInWithGoogle,\n resetPassword,\n updateProfile,\n sendEmailVerificationFn,\n clearError,\n refreshUserData,\n ]\n );\n\n // Show loading component while determining auth state\n if (loading && loadingComponent) {\n return <>{loadingComponent}</>;\n }\n\n return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;\n}\n\n/**\n * Hook to access auth context\n * Must be used within an AuthProvider\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { user, signOut, isAuthenticated } = useAuthContext();\n * \n * if (!isAuthenticated) {\n * return <LoginForm />;\n * }\n * \n * return (\n * <div>\n * <p>Welcome, {user?.displayName}</p>\n * <button onClick={signOut}>Sign Out</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useAuthContext(): AuthContextValue {\n const context = useContext(AuthContext);\n \n if (context === undefined) {\n throw new Error('useAuthContext must be used within an AuthProvider');\n }\n \n return context;\n}\n\n/**\n * HOC for protecting routes that require authentication\n * Redirects to login or shows loading state\n */\nexport function withAuth<P extends object>(\n WrappedComponent: React.ComponentType<P>,\n options: {\n LoadingComponent?: React.ComponentType;\n UnauthenticatedComponent?: React.ComponentType;\n } = {}\n) {\n const { LoadingComponent, UnauthenticatedComponent } = options;\n\n return function WithAuthComponent(props: P) {\n const { user, loading, isAuthenticated } = useAuthContext();\n\n if (loading) {\n return LoadingComponent ? <LoadingComponent /> : <div>Loading...</div>;\n }\n\n if (!isAuthenticated) {\n return UnauthenticatedComponent ? (\n <UnauthenticatedComponent />\n ) : (\n <div>Please sign in to continue</div>\n );\n }\n\n return <WrappedComponent {...props} />;\n };\n}\n\n","'use client';\n\nimport React, { useEffect, ReactNode } from 'react';\nimport { useAuthContext } from './AuthContext';\n\nexport interface AdminRouteProps {\n children: ReactNode;\n /** URL to redirect non-admin users to (default: '/') */\n redirectTo?: string;\n /** Custom loading component */\n loadingComponent?: ReactNode;\n /** Custom access denied component (shown briefly before redirect) */\n accessDeniedComponent?: ReactNode;\n /** If true, also allows 'employee' role (uses isStaff instead of isAdmin) */\n allowStaff?: boolean;\n /** Callback when access is denied */\n onAccessDenied?: () => void;\n}\n\n/**\n * Wrapper component that protects routes for admin-only access\n * Redirects non-admin users to the specified path\n * \n * @example\n * ```tsx\n * // app/admin/page.tsx\n * import { AdminRoute } from '@marcwelti/mw-core';\n * \n * export default function AdminPage() {\n * return (\n * <AdminRoute redirectTo=\"/login\">\n * <AdminDashboard />\n * </AdminRoute>\n * );\n * }\n * ```\n */\nexport function AdminRoute({\n children,\n redirectTo = '/',\n loadingComponent,\n accessDeniedComponent,\n allowStaff = false,\n onAccessDenied,\n}: AdminRouteProps) {\n const { user, loading, userDataLoading, isAdmin, isStaff } = useAuthContext();\n\n const hasAccess = allowStaff ? isStaff : isAdmin;\n const isLoading = loading || userDataLoading;\n\n useEffect(() => {\n // Only redirect after loading is complete\n if (isLoading) return;\n\n // Not authenticated or no access\n if (!user || !hasAccess) {\n onAccessDenied?.();\n \n // Use window.location for redirect to work in all environments\n if (typeof window !== 'undefined') {\n window.location.href = redirectTo;\n }\n }\n }, [user, isLoading, hasAccess, redirectTo, onAccessDenied]);\n\n // Show loading state\n if (isLoading) {\n return loadingComponent ? (\n <>{loadingComponent}</>\n ) : (\n <div className=\"flex min-h-screen items-center justify-center\">\n <div className=\"h-8 w-8 animate-spin rounded-full border-4 border-primary border-t-transparent\" />\n </div>\n );\n }\n\n // Not authenticated or no access - show nothing or access denied component while redirecting\n if (!user || !hasAccess) {\n return accessDeniedComponent ? <>{accessDeniedComponent}</> : null;\n }\n\n // User has access\n return <>{children}</>;\n}\n\n/**\n * Wrapper component that protects routes for staff-only access (owner, admin, or employee)\n * Convenience wrapper around AdminRoute with allowStaff=true\n */\nexport function StaffRoute(props: Omit<AdminRouteProps, 'allowStaff'>) {\n return <AdminRoute {...props} allowStaff />;\n}\n\n/**\n * Wrapper component that protects routes for owner-only access\n */\nexport function OwnerRoute({\n children,\n redirectTo = '/',\n loadingComponent,\n accessDeniedComponent,\n onAccessDenied,\n}: Omit<AdminRouteProps, 'allowStaff'>) {\n const { user, loading, userDataLoading, isOwner } = useAuthContext();\n\n const isLoading = loading || userDataLoading;\n\n useEffect(() => {\n if (isLoading) return;\n\n if (!user || !isOwner) {\n onAccessDenied?.();\n \n if (typeof window !== 'undefined') {\n window.location.href = redirectTo;\n }\n }\n }, [user, isLoading, isOwner, redirectTo, onAccessDenied]);\n\n if (isLoading) {\n return loadingComponent ? (\n <>{loadingComponent}</>\n ) : (\n <div className=\"flex min-h-screen items-center justify-center\">\n <div className=\"h-8 w-8 animate-spin rounded-full border-4 border-primary border-t-transparent\" />\n </div>\n );\n }\n\n if (!user || !isOwner) {\n return accessDeniedComponent ? <>{accessDeniedComponent}</> : null;\n }\n\n return <>{children}</>;\n}\n\n"]}
|
package/dist/context/index.mjs
CHANGED
|
@@ -57,7 +57,12 @@ function getFirebaseAuth() {
|
|
|
57
57
|
}
|
|
58
58
|
function getFirebaseFirestore() {
|
|
59
59
|
if (!_db) {
|
|
60
|
-
|
|
60
|
+
const databaseId = process.env.NEXT_PUBLIC_FIRESTORE_DATABASE_ID;
|
|
61
|
+
if (databaseId) {
|
|
62
|
+
_db = getFirestore(getFirebaseApp(), databaseId);
|
|
63
|
+
} else {
|
|
64
|
+
_db = getFirestore(getFirebaseApp());
|
|
65
|
+
}
|
|
61
66
|
}
|
|
62
67
|
return _db;
|
|
63
68
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/firebase/config.ts","../../src/firebase/auth.ts","../../src/firebase/firestore.ts","../../src/context/AuthContext.tsx","../../src/context/AdminRoute.tsx"],"names":["auth","firebaseSignOut","db","error","signOut","signInWithGoogle","resetPassword","updateProfile","useEffect","jsx","Fragment"],"mappings":"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","file":"index.mjs","sourcesContent":["import { initializeApp, getApps, getApp, FirebaseApp } from 'firebase/app';\nimport { getAuth, Auth } from 'firebase/auth';\nimport { getFirestore, Firestore } from 'firebase/firestore';\nimport { getStorage, FirebaseStorage } from 'firebase/storage';\n\n/**\n * Firebase configuration interface\n * All values should be provided via environment variables\n */\nexport interface FirebaseConfig {\n apiKey: string;\n authDomain: string;\n projectId: string;\n storageBucket: string;\n messagingSenderId: string;\n appId: string;\n measurementId?: string;\n}\n\n/**\n * Get Firebase configuration from environment variables\n * Works with Next.js NEXT_PUBLIC_ prefix\n */\nexport function getFirebaseConfig(): FirebaseConfig {\n const config: FirebaseConfig = {\n apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY || '',\n authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN || '',\n projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID || '',\n storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET || '',\n messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID || '',\n appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID || '',\n measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID,\n };\n\n // Validate required fields\n const requiredFields: (keyof FirebaseConfig)[] = [\n 'apiKey',\n 'authDomain',\n 'projectId',\n 'storageBucket',\n 'messagingSenderId',\n 'appId',\n ];\n\n const missingFields = requiredFields.filter((field) => !config[field]);\n\n if (missingFields.length > 0) {\n console.warn(\n `[mw-core] Missing Firebase config fields: ${missingFields.join(', ')}. ` +\n `Make sure to set NEXT_PUBLIC_FIREBASE_* environment variables.`\n );\n }\n\n return config;\n}\n\n/**\n * Initialize Firebase app (singleton pattern)\n * Safe to call multiple times - returns existing instance if already initialized\n */\nexport function initializeFirebase(config?: FirebaseConfig): FirebaseApp {\n if (getApps().length > 0) {\n return getApp();\n }\n\n const firebaseConfig = config || getFirebaseConfig();\n return initializeApp(firebaseConfig);\n}\n\n// Lazy initialization singletons\nlet _app: FirebaseApp | null = null;\nlet _auth: Auth | null = null;\nlet _db: Firestore | null = null;\nlet _storage: FirebaseStorage | null = null;\n\n/**\n * Get the Firebase app instance\n */\nexport function getFirebaseApp(): FirebaseApp {\n if (!_app) {\n _app = initializeFirebase();\n }\n return _app;\n}\n\n/**\n * Get the Firebase Auth instance\n */\nexport function getFirebaseAuth(): Auth {\n if (!_auth) {\n _auth = getAuth(getFirebaseApp());\n }\n return _auth;\n}\n\n/**\n * Get the Firestore database instance\n */\nexport function getFirebaseFirestore(): Firestore {\n if (!_db) {\n _db = getFirestore(getFirebaseApp());\n }\n return _db;\n}\n\n/**\n * Get the Firebase Storage instance\n */\nexport function getFirebaseStorage(): FirebaseStorage {\n if (!_storage) {\n _storage = getStorage(getFirebaseApp());\n }\n return _storage;\n}\n\n// Convenience exports for direct access\nexport const app = typeof window !== 'undefined' ? getFirebaseApp() : null;\nexport const auth = typeof window !== 'undefined' ? getFirebaseAuth() : null;\nexport const db = typeof window !== 'undefined' ? getFirebaseFirestore() : null;\nexport const storage = typeof window !== 'undefined' ? getFirebaseStorage() : null;\n\n","import {\n signInWithEmailAndPassword,\n createUserWithEmailAndPassword,\n signOut as firebaseSignOut,\n sendPasswordResetEmail,\n sendEmailVerification,\n updateProfile,\n GoogleAuthProvider,\n signInWithPopup,\n signInWithRedirect,\n getRedirectResult,\n onAuthStateChanged,\n User,\n UserCredential,\n Auth,\n} from 'firebase/auth';\nimport { getFirebaseAuth } from './config';\n\n/**\n * Sign in with email and password\n */\nexport async function signInWithEmail(\n email: string,\n password: string\n): Promise<UserCredential> {\n const auth = getFirebaseAuth();\n return signInWithEmailAndPassword(auth, email, password);\n}\n\n/**\n * Create a new account with email and password\n */\nexport async function signUpWithEmail(\n email: string,\n password: string,\n displayName?: string\n): Promise<UserCredential> {\n const auth = getFirebaseAuth();\n const credential = await createUserWithEmailAndPassword(auth, email, password);\n \n if (displayName && credential.user) {\n await updateProfile(credential.user, { displayName });\n }\n \n return credential;\n}\n\n/**\n * Sign out the current user\n */\nexport async function signOut(): Promise<void> {\n const auth = getFirebaseAuth();\n return firebaseSignOut(auth);\n}\n\n/**\n * Send a password reset email\n */\nexport async function resetPassword(email: string): Promise<void> {\n const auth = getFirebaseAuth();\n return sendPasswordResetEmail(auth, email);\n}\n\n/**\n * Send email verification to the current user\n */\nexport async function sendVerificationEmail(user: User): Promise<void> {\n return sendEmailVerification(user);\n}\n\n/**\n * Update the current user's profile\n */\nexport async function updateUserProfile(\n user: User,\n profile: { displayName?: string; photoURL?: string }\n): Promise<void> {\n return updateProfile(user, profile);\n}\n\n/**\n * Sign in with Google using popup\n */\nexport async function signInWithGoogle(): Promise<UserCredential> {\n const auth = getFirebaseAuth();\n const provider = new GoogleAuthProvider();\n provider.addScope('email');\n provider.addScope('profile');\n return signInWithPopup(auth, provider);\n}\n\n/**\n * Sign in with Google using redirect (better for mobile)\n */\nexport async function signInWithGoogleRedirect(): Promise<void> {\n const auth = getFirebaseAuth();\n const provider = new GoogleAuthProvider();\n provider.addScope('email');\n provider.addScope('profile');\n return signInWithRedirect(auth, provider);\n}\n\n/**\n * Get the result of a redirect sign-in\n */\nexport async function getGoogleRedirectResult(): Promise<UserCredential | null> {\n const auth = getFirebaseAuth();\n return getRedirectResult(auth);\n}\n\n/**\n * Subscribe to auth state changes\n */\nexport function subscribeToAuthState(\n callback: (user: User | null) => void\n): () => void {\n const auth = getFirebaseAuth();\n return onAuthStateChanged(auth, callback);\n}\n\n/**\n * Get the current user (may be null if not authenticated)\n */\nexport function getCurrentUser(): User | null {\n const auth = getFirebaseAuth();\n return auth.currentUser;\n}\n\n/**\n * Wait for the auth state to be determined\n * Useful for SSR/initial load\n */\nexport function waitForAuthState(): Promise<User | null> {\n return new Promise((resolve) => {\n const auth = getFirebaseAuth();\n const unsubscribe = onAuthStateChanged(auth, (user) => {\n unsubscribe();\n resolve(user);\n });\n });\n}\n\n// Re-export useful types\nexport type { User, UserCredential, Auth };\n\n","import {\n collection,\n doc,\n getDoc,\n getDocs,\n setDoc,\n updateDoc,\n deleteDoc,\n addDoc,\n query,\n where,\n orderBy,\n limit,\n startAfter,\n onSnapshot,\n DocumentData,\n DocumentReference,\n CollectionReference,\n QueryConstraint,\n DocumentSnapshot,\n QuerySnapshot,\n Timestamp,\n serverTimestamp,\n FieldValue,\n WhereFilterOp,\n OrderByDirection,\n Unsubscribe,\n} from 'firebase/firestore';\nimport { getFirebaseFirestore } from './config';\n\n/**\n * Get a document reference\n */\nexport function getDocRef<T = DocumentData>(\n collectionPath: string,\n documentId: string\n): DocumentReference<T> {\n const db = getFirebaseFirestore();\n return doc(db, collectionPath, documentId) as DocumentReference<T>;\n}\n\n/**\n * Get a collection reference\n */\nexport function getCollectionRef<T = DocumentData>(\n collectionPath: string\n): CollectionReference<T> {\n const db = getFirebaseFirestore();\n return collection(db, collectionPath) as CollectionReference<T>;\n}\n\n/**\n * Get a single document by ID\n */\nexport async function getDocument<T = DocumentData>(\n collectionPath: string,\n documentId: string\n): Promise<T | null> {\n const docRef = getDocRef<T>(collectionPath, documentId);\n const docSnap = await getDoc(docRef);\n \n if (docSnap.exists()) {\n return { id: docSnap.id, ...docSnap.data() } as T;\n }\n \n return null;\n}\n\n/**\n * Get all documents in a collection\n */\nexport async function getCollection<T = DocumentData>(\n collectionPath: string,\n constraints: QueryConstraint[] = []\n): Promise<T[]> {\n const collectionRef = getCollectionRef<T>(collectionPath);\n const q = query(collectionRef, ...constraints);\n const querySnapshot = await getDocs(q);\n \n return querySnapshot.docs.map((doc) => ({\n id: doc.id,\n ...doc.data(),\n })) as T[];\n}\n\n/**\n * Add a new document with auto-generated ID\n */\nexport async function addDocument<T = DocumentData>(\n collectionPath: string,\n data: Omit<T, 'id'>\n): Promise<string> {\n const collectionRef = getCollectionRef(collectionPath);\n const docRef = await addDoc(collectionRef, {\n ...data,\n createdAt: serverTimestamp(),\n updatedAt: serverTimestamp(),\n });\n return docRef.id;\n}\n\n/**\n * Set/create a document with a specific ID\n */\nexport async function setDocument<T = DocumentData>(\n collectionPath: string,\n documentId: string,\n data: Omit<T, 'id'>,\n merge: boolean = false\n): Promise<void> {\n const docRef = getDocRef(collectionPath, documentId);\n await setDoc(\n docRef,\n {\n ...data,\n updatedAt: serverTimestamp(),\n ...(merge ? {} : { createdAt: serverTimestamp() }),\n },\n { merge }\n );\n}\n\n/**\n * Update specific fields in a document\n */\nexport async function updateDocument<T = DocumentData>(\n collectionPath: string,\n documentId: string,\n data: Partial<T>\n): Promise<void> {\n const docRef = getDocRef(collectionPath, documentId);\n await updateDoc(docRef, {\n ...data,\n updatedAt: serverTimestamp(),\n });\n}\n\n/**\n * Delete a document\n */\nexport async function deleteDocument(\n collectionPath: string,\n documentId: string\n): Promise<void> {\n const docRef = getDocRef(collectionPath, documentId);\n await deleteDoc(docRef);\n}\n\n/**\n * Subscribe to a single document\n */\nexport function subscribeToDocument<T = DocumentData>(\n collectionPath: string,\n documentId: string,\n callback: (data: T | null) => void\n): Unsubscribe {\n const docRef = getDocRef<T>(collectionPath, documentId);\n \n return onSnapshot(docRef, (docSnap) => {\n if (docSnap.exists()) {\n callback({ id: docSnap.id, ...docSnap.data() } as T);\n } else {\n callback(null);\n }\n });\n}\n\n/**\n * Subscribe to a collection\n */\nexport function subscribeToCollection<T = DocumentData>(\n collectionPath: string,\n callback: (data: T[]) => void,\n constraints: QueryConstraint[] = []\n): Unsubscribe {\n const collectionRef = getCollectionRef<T>(collectionPath);\n const q = query(collectionRef, ...constraints);\n \n return onSnapshot(q, (querySnapshot) => {\n const data = querySnapshot.docs.map((doc) => ({\n id: doc.id,\n ...doc.data(),\n })) as T[];\n callback(data);\n });\n}\n\n// Export query helpers for convenience\nexport {\n query,\n where,\n orderBy,\n limit,\n startAfter,\n Timestamp,\n serverTimestamp,\n};\n\n// Re-export types\nexport type {\n DocumentData,\n DocumentReference,\n CollectionReference,\n QueryConstraint,\n DocumentSnapshot,\n QuerySnapshot,\n FieldValue,\n WhereFilterOp,\n OrderByDirection,\n Unsubscribe,\n};\n\n","'use client';\n\nimport React, {\n createContext,\n useContext,\n useState,\n useEffect,\n useCallback,\n useMemo,\n ReactNode,\n} from 'react';\nimport { User, UserCredential } from 'firebase/auth';\nimport {\n subscribeToAuthState,\n signInWithEmail,\n signUpWithEmail,\n signOut as firebaseSignOut,\n signInWithGoogle as firebaseSignInWithGoogle,\n resetPassword as firebaseResetPassword,\n updateUserProfile,\n sendVerificationEmail,\n} from '../firebase/auth';\nimport { subscribeToDocument } from '../firebase/firestore';\nimport type { UserRole } from '../types';\n\n/**\n * User data from Firestore (includes role and additional profile data)\n */\nexport interface UserData {\n id: string;\n uid: string;\n email: string | null;\n displayName: string | null;\n photoURL: string | null;\n role: UserRole | null;\n phoneNumber?: string | null;\n isActive?: boolean;\n}\n\nexport interface AuthContextValue {\n /** The current authenticated Firebase user, or null if not authenticated */\n user: User | null;\n /** User data from Firestore (includes role) */\n userData: UserData | null;\n /** The user's role from Firestore */\n role: UserRole | null;\n /** Whether the auth state is still loading */\n loading: boolean;\n /** Whether the user data is still loading from Firestore */\n userDataLoading: boolean;\n /** Whether the user is authenticated */\n isAuthenticated: boolean;\n /** Whether the user has admin access (owner or admin role) */\n isAdmin: boolean;\n /** Whether the user is staff (owner, admin, or employee role) */\n isStaff: boolean;\n /** Whether the user is the owner */\n isOwner: boolean;\n /** Any auth error that occurred */\n error: Error | null;\n /** Sign in with email and password */\n signIn: (email: string, password: string) => Promise<UserCredential>;\n /** Create a new account with email and password */\n signUp: (email: string, password: string, displayName?: string) => Promise<UserCredential>;\n /** Sign out the current user */\n signOut: () => Promise<void>;\n /** Sign in with Google */\n signInWithGoogle: () => Promise<UserCredential>;\n /** Send a password reset email */\n resetPassword: (email: string) => Promise<void>;\n /** Update the user's profile */\n updateProfile: (profile: { displayName?: string; photoURL?: string }) => Promise<void>;\n /** Send email verification */\n sendEmailVerification: () => Promise<void>;\n /** Clear any auth errors */\n clearError: () => void;\n /** Refresh user data from Firestore */\n refreshUserData: () => void;\n}\n\nconst AuthContext = createContext<AuthContextValue | undefined>(undefined);\n\nexport interface AuthProviderProps {\n children: ReactNode;\n /** Optional: Called when auth state changes */\n onAuthStateChange?: (user: User | null) => void;\n /** Optional: Called when user data changes */\n onUserDataChange?: (userData: UserData | null) => void;\n /** Optional: Loading component to show while auth state is loading */\n loadingComponent?: ReactNode;\n /** Optional: Collection path for user documents (default: 'users') */\n usersCollection?: string;\n}\n\n/**\n * Auth Provider component that wraps your app and provides auth context\n * Automatically fetches user data from Firestore including role\n * \n * @example\n * ```tsx\n * // app/layout.tsx\n * import { AuthProvider } from '@marcwelti/mw-core';\n * \n * export default function RootLayout({ children }) {\n * return (\n * <html>\n * <body>\n * <AuthProvider>\n * {children}\n * </AuthProvider>\n * </body>\n * </html>\n * );\n * }\n * ```\n */\nexport function AuthProvider({\n children,\n onAuthStateChange,\n onUserDataChange,\n loadingComponent,\n usersCollection = 'users',\n}: AuthProviderProps) {\n const [user, setUser] = useState<User | null>(null);\n const [userData, setUserData] = useState<UserData | null>(null);\n const [loading, setLoading] = useState(true);\n const [userDataLoading, setUserDataLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [refreshTrigger, setRefreshTrigger] = useState(0);\n\n // Subscribe to Firebase Auth state\n useEffect(() => {\n const unsubscribe = subscribeToAuthState((firebaseUser) => {\n setUser(firebaseUser);\n setLoading(false);\n onAuthStateChange?.(firebaseUser);\n \n // Clear user data when logged out\n if (!firebaseUser) {\n setUserData(null);\n onUserDataChange?.(null);\n }\n });\n\n return unsubscribe;\n }, [onAuthStateChange, onUserDataChange]);\n\n // Subscribe to Firestore user document when authenticated\n useEffect(() => {\n if (!user) {\n setUserData(null);\n setUserDataLoading(false);\n return;\n }\n\n setUserDataLoading(true);\n\n const unsubscribe = subscribeToDocument<UserData>(\n usersCollection,\n user.uid,\n (data) => {\n if (data) {\n const mergedData: UserData = {\n id: user.uid,\n uid: user.uid,\n email: user.email,\n displayName: data.displayName || user.displayName,\n photoURL: data.photoURL || user.photoURL,\n role: data.role || null,\n phoneNumber: data.phoneNumber || user.phoneNumber,\n isActive: data.isActive,\n };\n setUserData(mergedData);\n onUserDataChange?.(mergedData);\n } else {\n // User document doesn't exist - create basic data without role\n const basicData: UserData = {\n id: user.uid,\n uid: user.uid,\n email: user.email,\n displayName: user.displayName,\n photoURL: user.photoURL,\n role: null,\n phoneNumber: user.phoneNumber,\n };\n setUserData(basicData);\n onUserDataChange?.(basicData);\n }\n setUserDataLoading(false);\n }\n );\n\n return unsubscribe;\n }, [user, usersCollection, onUserDataChange, refreshTrigger]);\n\n // Refresh user data function\n const refreshUserData = useCallback(() => {\n setRefreshTrigger((prev) => prev + 1);\n }, []);\n\n const signIn = useCallback(async (email: string, password: string) => {\n setError(null);\n try {\n return await signInWithEmail(email, password);\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n }, []);\n\n const signUp = useCallback(\n async (email: string, password: string, displayName?: string) => {\n setError(null);\n try {\n return await signUpWithEmail(email, password, displayName);\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n },\n []\n );\n\n const signOut = useCallback(async () => {\n setError(null);\n try {\n await firebaseSignOut();\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n }, []);\n\n const signInWithGoogle = useCallback(async () => {\n setError(null);\n try {\n return await firebaseSignInWithGoogle();\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n }, []);\n\n const resetPassword = useCallback(async (email: string) => {\n setError(null);\n try {\n await firebaseResetPassword(email);\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n }, []);\n\n const updateProfile = useCallback(\n async (profile: { displayName?: string; photoURL?: string }) => {\n setError(null);\n if (!user) {\n const error = new Error('No authenticated user');\n setError(error);\n throw error;\n }\n try {\n await updateUserProfile(user, profile);\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n },\n [user]\n );\n\n const sendEmailVerificationFn = useCallback(async () => {\n setError(null);\n if (!user) {\n const error = new Error('No authenticated user');\n setError(error);\n throw error;\n }\n try {\n await sendVerificationEmail(user);\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n }, [user]);\n\n const clearError = useCallback(() => {\n setError(null);\n }, []);\n\n // Computed role-based properties\n const role = userData?.role ?? null;\n const isAdmin = role === 'owner' || role === 'admin';\n const isStaff = role === 'owner' || role === 'admin' || role === 'employee';\n const isOwner = role === 'owner';\n\n const value = useMemo<AuthContextValue>(\n () => ({\n user,\n userData,\n role,\n loading,\n userDataLoading,\n isAuthenticated: !!user,\n isAdmin,\n isStaff,\n isOwner,\n error,\n signIn,\n signUp,\n signOut,\n signInWithGoogle,\n resetPassword,\n updateProfile,\n sendEmailVerification: sendEmailVerificationFn,\n clearError,\n refreshUserData,\n }),\n [\n user,\n userData,\n role,\n loading,\n userDataLoading,\n isAdmin,\n isStaff,\n isOwner,\n error,\n signIn,\n signUp,\n signOut,\n signInWithGoogle,\n resetPassword,\n updateProfile,\n sendEmailVerificationFn,\n clearError,\n refreshUserData,\n ]\n );\n\n // Show loading component while determining auth state\n if (loading && loadingComponent) {\n return <>{loadingComponent}</>;\n }\n\n return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;\n}\n\n/**\n * Hook to access auth context\n * Must be used within an AuthProvider\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { user, signOut, isAuthenticated } = useAuthContext();\n * \n * if (!isAuthenticated) {\n * return <LoginForm />;\n * }\n * \n * return (\n * <div>\n * <p>Welcome, {user?.displayName}</p>\n * <button onClick={signOut}>Sign Out</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useAuthContext(): AuthContextValue {\n const context = useContext(AuthContext);\n \n if (context === undefined) {\n throw new Error('useAuthContext must be used within an AuthProvider');\n }\n \n return context;\n}\n\n/**\n * HOC for protecting routes that require authentication\n * Redirects to login or shows loading state\n */\nexport function withAuth<P extends object>(\n WrappedComponent: React.ComponentType<P>,\n options: {\n LoadingComponent?: React.ComponentType;\n UnauthenticatedComponent?: React.ComponentType;\n } = {}\n) {\n const { LoadingComponent, UnauthenticatedComponent } = options;\n\n return function WithAuthComponent(props: P) {\n const { user, loading, isAuthenticated } = useAuthContext();\n\n if (loading) {\n return LoadingComponent ? <LoadingComponent /> : <div>Loading...</div>;\n }\n\n if (!isAuthenticated) {\n return UnauthenticatedComponent ? (\n <UnauthenticatedComponent />\n ) : (\n <div>Please sign in to continue</div>\n );\n }\n\n return <WrappedComponent {...props} />;\n };\n}\n\n","'use client';\n\nimport React, { useEffect, ReactNode } from 'react';\nimport { useAuthContext } from './AuthContext';\n\nexport interface AdminRouteProps {\n children: ReactNode;\n /** URL to redirect non-admin users to (default: '/') */\n redirectTo?: string;\n /** Custom loading component */\n loadingComponent?: ReactNode;\n /** Custom access denied component (shown briefly before redirect) */\n accessDeniedComponent?: ReactNode;\n /** If true, also allows 'employee' role (uses isStaff instead of isAdmin) */\n allowStaff?: boolean;\n /** Callback when access is denied */\n onAccessDenied?: () => void;\n}\n\n/**\n * Wrapper component that protects routes for admin-only access\n * Redirects non-admin users to the specified path\n * \n * @example\n * ```tsx\n * // app/admin/page.tsx\n * import { AdminRoute } from '@marcwelti/mw-core';\n * \n * export default function AdminPage() {\n * return (\n * <AdminRoute redirectTo=\"/login\">\n * <AdminDashboard />\n * </AdminRoute>\n * );\n * }\n * ```\n */\nexport function AdminRoute({\n children,\n redirectTo = '/',\n loadingComponent,\n accessDeniedComponent,\n allowStaff = false,\n onAccessDenied,\n}: AdminRouteProps) {\n const { user, loading, userDataLoading, isAdmin, isStaff } = useAuthContext();\n\n const hasAccess = allowStaff ? isStaff : isAdmin;\n const isLoading = loading || userDataLoading;\n\n useEffect(() => {\n // Only redirect after loading is complete\n if (isLoading) return;\n\n // Not authenticated or no access\n if (!user || !hasAccess) {\n onAccessDenied?.();\n \n // Use window.location for redirect to work in all environments\n if (typeof window !== 'undefined') {\n window.location.href = redirectTo;\n }\n }\n }, [user, isLoading, hasAccess, redirectTo, onAccessDenied]);\n\n // Show loading state\n if (isLoading) {\n return loadingComponent ? (\n <>{loadingComponent}</>\n ) : (\n <div className=\"flex min-h-screen items-center justify-center\">\n <div className=\"h-8 w-8 animate-spin rounded-full border-4 border-primary border-t-transparent\" />\n </div>\n );\n }\n\n // Not authenticated or no access - show nothing or access denied component while redirecting\n if (!user || !hasAccess) {\n return accessDeniedComponent ? <>{accessDeniedComponent}</> : null;\n }\n\n // User has access\n return <>{children}</>;\n}\n\n/**\n * Wrapper component that protects routes for staff-only access (owner, admin, or employee)\n * Convenience wrapper around AdminRoute with allowStaff=true\n */\nexport function StaffRoute(props: Omit<AdminRouteProps, 'allowStaff'>) {\n return <AdminRoute {...props} allowStaff />;\n}\n\n/**\n * Wrapper component that protects routes for owner-only access\n */\nexport function OwnerRoute({\n children,\n redirectTo = '/',\n loadingComponent,\n accessDeniedComponent,\n onAccessDenied,\n}: Omit<AdminRouteProps, 'allowStaff'>) {\n const { user, loading, userDataLoading, isOwner } = useAuthContext();\n\n const isLoading = loading || userDataLoading;\n\n useEffect(() => {\n if (isLoading) return;\n\n if (!user || !isOwner) {\n onAccessDenied?.();\n \n if (typeof window !== 'undefined') {\n window.location.href = redirectTo;\n }\n }\n }, [user, isLoading, isOwner, redirectTo, onAccessDenied]);\n\n if (isLoading) {\n return loadingComponent ? (\n <>{loadingComponent}</>\n ) : (\n <div className=\"flex min-h-screen items-center justify-center\">\n <div className=\"h-8 w-8 animate-spin rounded-full border-4 border-primary border-t-transparent\" />\n </div>\n );\n }\n\n if (!user || !isOwner) {\n return accessDeniedComponent ? <>{accessDeniedComponent}</> : null;\n }\n\n return <>{children}</>;\n}\n\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/firebase/config.ts","../../src/firebase/auth.ts","../../src/firebase/firestore.ts","../../src/context/AuthContext.tsx","../../src/context/AdminRoute.tsx"],"names":["auth","firebaseSignOut","db","error","signOut","signInWithGoogle","resetPassword","updateProfile","useEffect","jsx","Fragment"],"mappings":"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","file":"index.mjs","sourcesContent":["import { initializeApp, getApps, getApp, FirebaseApp } from 'firebase/app';\nimport { getAuth, Auth } from 'firebase/auth';\nimport { getFirestore, Firestore } from 'firebase/firestore';\nimport { getStorage, FirebaseStorage } from 'firebase/storage';\n\n/**\n * Firebase configuration interface\n * All values should be provided via environment variables\n */\nexport interface FirebaseConfig {\n apiKey: string;\n authDomain: string;\n projectId: string;\n storageBucket: string;\n messagingSenderId: string;\n appId: string;\n measurementId?: string;\n}\n\n/**\n * Get Firebase configuration from environment variables\n * Works with Next.js NEXT_PUBLIC_ prefix\n */\nexport function getFirebaseConfig(): FirebaseConfig {\n const config: FirebaseConfig = {\n apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY || '',\n authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN || '',\n projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID || '',\n storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET || '',\n messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID || '',\n appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID || '',\n measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID,\n };\n\n // Validate required fields\n const requiredFields: (keyof FirebaseConfig)[] = [\n 'apiKey',\n 'authDomain',\n 'projectId',\n 'storageBucket',\n 'messagingSenderId',\n 'appId',\n ];\n\n const missingFields = requiredFields.filter((field) => !config[field]);\n\n if (missingFields.length > 0) {\n console.warn(\n `[mw-core] Missing Firebase config fields: ${missingFields.join(', ')}. ` +\n `Make sure to set NEXT_PUBLIC_FIREBASE_* environment variables.`\n );\n }\n\n return config;\n}\n\n/**\n * Initialize Firebase app (singleton pattern)\n * Safe to call multiple times - returns existing instance if already initialized\n */\nexport function initializeFirebase(config?: FirebaseConfig): FirebaseApp {\n if (getApps().length > 0) {\n return getApp();\n }\n\n const firebaseConfig = config || getFirebaseConfig();\n return initializeApp(firebaseConfig);\n}\n\n// Lazy initialization singletons\nlet _app: FirebaseApp | null = null;\nlet _auth: Auth | null = null;\nlet _db: Firestore | null = null;\nlet _storage: FirebaseStorage | null = null;\n\n/**\n * Get the Firebase app instance\n */\nexport function getFirebaseApp(): FirebaseApp {\n if (!_app) {\n _app = initializeFirebase();\n }\n return _app;\n}\n\n/**\n * Get the Firebase Auth instance\n */\nexport function getFirebaseAuth(): Auth {\n if (!_auth) {\n _auth = getAuth(getFirebaseApp());\n }\n return _auth;\n}\n\n/**\n * Get the Firestore database instance\n * Supports named databases via NEXT_PUBLIC_FIRESTORE_DATABASE_ID env var\n */\nexport function getFirebaseFirestore(): Firestore {\n if (!_db) {\n const databaseId = process.env.NEXT_PUBLIC_FIRESTORE_DATABASE_ID;\n if (databaseId) {\n _db = getFirestore(getFirebaseApp(), databaseId);\n } else {\n _db = getFirestore(getFirebaseApp());\n }\n }\n return _db;\n}\n\n/**\n * Get the Firebase Storage instance\n */\nexport function getFirebaseStorage(): FirebaseStorage {\n if (!_storage) {\n _storage = getStorage(getFirebaseApp());\n }\n return _storage;\n}\n\n// Convenience exports for direct access\nexport const app = typeof window !== 'undefined' ? getFirebaseApp() : null;\nexport const auth = typeof window !== 'undefined' ? getFirebaseAuth() : null;\nexport const db = typeof window !== 'undefined' ? getFirebaseFirestore() : null;\nexport const storage = typeof window !== 'undefined' ? getFirebaseStorage() : null;\n\n","import {\n signInWithEmailAndPassword,\n createUserWithEmailAndPassword,\n signOut as firebaseSignOut,\n sendPasswordResetEmail,\n sendEmailVerification,\n updateProfile,\n GoogleAuthProvider,\n signInWithPopup,\n signInWithRedirect,\n getRedirectResult,\n onAuthStateChanged,\n User,\n UserCredential,\n Auth,\n} from 'firebase/auth';\nimport { getFirebaseAuth } from './config';\n\n/**\n * Sign in with email and password\n */\nexport async function signInWithEmail(\n email: string,\n password: string\n): Promise<UserCredential> {\n const auth = getFirebaseAuth();\n return signInWithEmailAndPassword(auth, email, password);\n}\n\n/**\n * Create a new account with email and password\n */\nexport async function signUpWithEmail(\n email: string,\n password: string,\n displayName?: string\n): Promise<UserCredential> {\n const auth = getFirebaseAuth();\n const credential = await createUserWithEmailAndPassword(auth, email, password);\n \n if (displayName && credential.user) {\n await updateProfile(credential.user, { displayName });\n }\n \n return credential;\n}\n\n/**\n * Sign out the current user\n */\nexport async function signOut(): Promise<void> {\n const auth = getFirebaseAuth();\n return firebaseSignOut(auth);\n}\n\n/**\n * Send a password reset email\n */\nexport async function resetPassword(email: string): Promise<void> {\n const auth = getFirebaseAuth();\n return sendPasswordResetEmail(auth, email);\n}\n\n/**\n * Send email verification to the current user\n */\nexport async function sendVerificationEmail(user: User): Promise<void> {\n return sendEmailVerification(user);\n}\n\n/**\n * Update the current user's profile\n */\nexport async function updateUserProfile(\n user: User,\n profile: { displayName?: string; photoURL?: string }\n): Promise<void> {\n return updateProfile(user, profile);\n}\n\n/**\n * Sign in with Google using popup\n */\nexport async function signInWithGoogle(): Promise<UserCredential> {\n const auth = getFirebaseAuth();\n const provider = new GoogleAuthProvider();\n provider.addScope('email');\n provider.addScope('profile');\n return signInWithPopup(auth, provider);\n}\n\n/**\n * Sign in with Google using redirect (better for mobile)\n */\nexport async function signInWithGoogleRedirect(): Promise<void> {\n const auth = getFirebaseAuth();\n const provider = new GoogleAuthProvider();\n provider.addScope('email');\n provider.addScope('profile');\n return signInWithRedirect(auth, provider);\n}\n\n/**\n * Get the result of a redirect sign-in\n */\nexport async function getGoogleRedirectResult(): Promise<UserCredential | null> {\n const auth = getFirebaseAuth();\n return getRedirectResult(auth);\n}\n\n/**\n * Subscribe to auth state changes\n */\nexport function subscribeToAuthState(\n callback: (user: User | null) => void\n): () => void {\n const auth = getFirebaseAuth();\n return onAuthStateChanged(auth, callback);\n}\n\n/**\n * Get the current user (may be null if not authenticated)\n */\nexport function getCurrentUser(): User | null {\n const auth = getFirebaseAuth();\n return auth.currentUser;\n}\n\n/**\n * Wait for the auth state to be determined\n * Useful for SSR/initial load\n */\nexport function waitForAuthState(): Promise<User | null> {\n return new Promise((resolve) => {\n const auth = getFirebaseAuth();\n const unsubscribe = onAuthStateChanged(auth, (user) => {\n unsubscribe();\n resolve(user);\n });\n });\n}\n\n// Re-export useful types\nexport type { User, UserCredential, Auth };\n\n","import {\n collection,\n doc,\n getDoc,\n getDocs,\n setDoc,\n updateDoc,\n deleteDoc,\n addDoc,\n query,\n where,\n orderBy,\n limit,\n startAfter,\n onSnapshot,\n DocumentData,\n DocumentReference,\n CollectionReference,\n QueryConstraint,\n DocumentSnapshot,\n QuerySnapshot,\n Timestamp,\n serverTimestamp,\n FieldValue,\n WhereFilterOp,\n OrderByDirection,\n Unsubscribe,\n} from 'firebase/firestore';\nimport { getFirebaseFirestore } from './config';\n\n/**\n * Get a document reference\n */\nexport function getDocRef<T = DocumentData>(\n collectionPath: string,\n documentId: string\n): DocumentReference<T> {\n const db = getFirebaseFirestore();\n return doc(db, collectionPath, documentId) as DocumentReference<T>;\n}\n\n/**\n * Get a collection reference\n */\nexport function getCollectionRef<T = DocumentData>(\n collectionPath: string\n): CollectionReference<T> {\n const db = getFirebaseFirestore();\n return collection(db, collectionPath) as CollectionReference<T>;\n}\n\n/**\n * Get a single document by ID\n */\nexport async function getDocument<T = DocumentData>(\n collectionPath: string,\n documentId: string\n): Promise<T | null> {\n const docRef = getDocRef<T>(collectionPath, documentId);\n const docSnap = await getDoc(docRef);\n \n if (docSnap.exists()) {\n return { id: docSnap.id, ...docSnap.data() } as T;\n }\n \n return null;\n}\n\n/**\n * Get all documents in a collection\n */\nexport async function getCollection<T = DocumentData>(\n collectionPath: string,\n constraints: QueryConstraint[] = []\n): Promise<T[]> {\n const collectionRef = getCollectionRef<T>(collectionPath);\n const q = query(collectionRef, ...constraints);\n const querySnapshot = await getDocs(q);\n \n return querySnapshot.docs.map((doc) => ({\n id: doc.id,\n ...doc.data(),\n })) as T[];\n}\n\n/**\n * Add a new document with auto-generated ID\n */\nexport async function addDocument<T = DocumentData>(\n collectionPath: string,\n data: Omit<T, 'id'>\n): Promise<string> {\n const collectionRef = getCollectionRef(collectionPath);\n const docRef = await addDoc(collectionRef, {\n ...data,\n createdAt: serverTimestamp(),\n updatedAt: serverTimestamp(),\n });\n return docRef.id;\n}\n\n/**\n * Set/create a document with a specific ID\n */\nexport async function setDocument<T = DocumentData>(\n collectionPath: string,\n documentId: string,\n data: Omit<T, 'id'>,\n merge: boolean = false\n): Promise<void> {\n const docRef = getDocRef(collectionPath, documentId);\n await setDoc(\n docRef,\n {\n ...data,\n updatedAt: serverTimestamp(),\n ...(merge ? {} : { createdAt: serverTimestamp() }),\n },\n { merge }\n );\n}\n\n/**\n * Update specific fields in a document\n */\nexport async function updateDocument<T = DocumentData>(\n collectionPath: string,\n documentId: string,\n data: Partial<T>\n): Promise<void> {\n const docRef = getDocRef(collectionPath, documentId);\n await updateDoc(docRef, {\n ...data,\n updatedAt: serverTimestamp(),\n });\n}\n\n/**\n * Delete a document\n */\nexport async function deleteDocument(\n collectionPath: string,\n documentId: string\n): Promise<void> {\n const docRef = getDocRef(collectionPath, documentId);\n await deleteDoc(docRef);\n}\n\n/**\n * Subscribe to a single document\n */\nexport function subscribeToDocument<T = DocumentData>(\n collectionPath: string,\n documentId: string,\n callback: (data: T | null) => void\n): Unsubscribe {\n const docRef = getDocRef<T>(collectionPath, documentId);\n \n return onSnapshot(docRef, (docSnap) => {\n if (docSnap.exists()) {\n callback({ id: docSnap.id, ...docSnap.data() } as T);\n } else {\n callback(null);\n }\n });\n}\n\n/**\n * Subscribe to a collection\n */\nexport function subscribeToCollection<T = DocumentData>(\n collectionPath: string,\n callback: (data: T[]) => void,\n constraints: QueryConstraint[] = []\n): Unsubscribe {\n const collectionRef = getCollectionRef<T>(collectionPath);\n const q = query(collectionRef, ...constraints);\n \n return onSnapshot(q, (querySnapshot) => {\n const data = querySnapshot.docs.map((doc) => ({\n id: doc.id,\n ...doc.data(),\n })) as T[];\n callback(data);\n });\n}\n\n// Export query helpers for convenience\nexport {\n query,\n where,\n orderBy,\n limit,\n startAfter,\n Timestamp,\n serverTimestamp,\n};\n\n// Re-export types\nexport type {\n DocumentData,\n DocumentReference,\n CollectionReference,\n QueryConstraint,\n DocumentSnapshot,\n QuerySnapshot,\n FieldValue,\n WhereFilterOp,\n OrderByDirection,\n Unsubscribe,\n};\n\n","'use client';\n\nimport React, {\n createContext,\n useContext,\n useState,\n useEffect,\n useCallback,\n useMemo,\n ReactNode,\n} from 'react';\nimport { User, UserCredential } from 'firebase/auth';\nimport {\n subscribeToAuthState,\n signInWithEmail,\n signUpWithEmail,\n signOut as firebaseSignOut,\n signInWithGoogle as firebaseSignInWithGoogle,\n resetPassword as firebaseResetPassword,\n updateUserProfile,\n sendVerificationEmail,\n} from '../firebase/auth';\nimport { subscribeToDocument } from '../firebase/firestore';\nimport type { UserRole } from '../types';\n\n/**\n * User data from Firestore (includes role and additional profile data)\n */\nexport interface UserData {\n id: string;\n uid: string;\n email: string | null;\n displayName: string | null;\n photoURL: string | null;\n role: UserRole | null;\n phoneNumber?: string | null;\n isActive?: boolean;\n}\n\nexport interface AuthContextValue {\n /** The current authenticated Firebase user, or null if not authenticated */\n user: User | null;\n /** User data from Firestore (includes role) */\n userData: UserData | null;\n /** The user's role from Firestore */\n role: UserRole | null;\n /** Whether the auth state is still loading */\n loading: boolean;\n /** Whether the user data is still loading from Firestore */\n userDataLoading: boolean;\n /** Whether the user is authenticated */\n isAuthenticated: boolean;\n /** Whether the user has admin access (owner or admin role) */\n isAdmin: boolean;\n /** Whether the user is staff (owner, admin, or employee role) */\n isStaff: boolean;\n /** Whether the user is the owner */\n isOwner: boolean;\n /** Any auth error that occurred */\n error: Error | null;\n /** Sign in with email and password */\n signIn: (email: string, password: string) => Promise<UserCredential>;\n /** Create a new account with email and password */\n signUp: (email: string, password: string, displayName?: string) => Promise<UserCredential>;\n /** Sign out the current user */\n signOut: () => Promise<void>;\n /** Sign in with Google */\n signInWithGoogle: () => Promise<UserCredential>;\n /** Send a password reset email */\n resetPassword: (email: string) => Promise<void>;\n /** Update the user's profile */\n updateProfile: (profile: { displayName?: string; photoURL?: string }) => Promise<void>;\n /** Send email verification */\n sendEmailVerification: () => Promise<void>;\n /** Clear any auth errors */\n clearError: () => void;\n /** Refresh user data from Firestore */\n refreshUserData: () => void;\n}\n\nconst AuthContext = createContext<AuthContextValue | undefined>(undefined);\n\nexport interface AuthProviderProps {\n children: ReactNode;\n /** Optional: Called when auth state changes */\n onAuthStateChange?: (user: User | null) => void;\n /** Optional: Called when user data changes */\n onUserDataChange?: (userData: UserData | null) => void;\n /** Optional: Loading component to show while auth state is loading */\n loadingComponent?: ReactNode;\n /** Optional: Collection path for user documents (default: 'users') */\n usersCollection?: string;\n}\n\n/**\n * Auth Provider component that wraps your app and provides auth context\n * Automatically fetches user data from Firestore including role\n * \n * @example\n * ```tsx\n * // app/layout.tsx\n * import { AuthProvider } from '@marcwelti/mw-core';\n * \n * export default function RootLayout({ children }) {\n * return (\n * <html>\n * <body>\n * <AuthProvider>\n * {children}\n * </AuthProvider>\n * </body>\n * </html>\n * );\n * }\n * ```\n */\nexport function AuthProvider({\n children,\n onAuthStateChange,\n onUserDataChange,\n loadingComponent,\n usersCollection = 'users',\n}: AuthProviderProps) {\n const [user, setUser] = useState<User | null>(null);\n const [userData, setUserData] = useState<UserData | null>(null);\n const [loading, setLoading] = useState(true);\n const [userDataLoading, setUserDataLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [refreshTrigger, setRefreshTrigger] = useState(0);\n\n // Subscribe to Firebase Auth state\n useEffect(() => {\n const unsubscribe = subscribeToAuthState((firebaseUser) => {\n setUser(firebaseUser);\n setLoading(false);\n onAuthStateChange?.(firebaseUser);\n \n // Clear user data when logged out\n if (!firebaseUser) {\n setUserData(null);\n onUserDataChange?.(null);\n }\n });\n\n return unsubscribe;\n }, [onAuthStateChange, onUserDataChange]);\n\n // Subscribe to Firestore user document when authenticated\n useEffect(() => {\n if (!user) {\n setUserData(null);\n setUserDataLoading(false);\n return;\n }\n\n setUserDataLoading(true);\n\n const unsubscribe = subscribeToDocument<UserData>(\n usersCollection,\n user.uid,\n (data) => {\n if (data) {\n const mergedData: UserData = {\n id: user.uid,\n uid: user.uid,\n email: user.email,\n displayName: data.displayName || user.displayName,\n photoURL: data.photoURL || user.photoURL,\n role: data.role || null,\n phoneNumber: data.phoneNumber || user.phoneNumber,\n isActive: data.isActive,\n };\n setUserData(mergedData);\n onUserDataChange?.(mergedData);\n } else {\n // User document doesn't exist - create basic data without role\n const basicData: UserData = {\n id: user.uid,\n uid: user.uid,\n email: user.email,\n displayName: user.displayName,\n photoURL: user.photoURL,\n role: null,\n phoneNumber: user.phoneNumber,\n };\n setUserData(basicData);\n onUserDataChange?.(basicData);\n }\n setUserDataLoading(false);\n }\n );\n\n return unsubscribe;\n }, [user, usersCollection, onUserDataChange, refreshTrigger]);\n\n // Refresh user data function\n const refreshUserData = useCallback(() => {\n setRefreshTrigger((prev) => prev + 1);\n }, []);\n\n const signIn = useCallback(async (email: string, password: string) => {\n setError(null);\n try {\n return await signInWithEmail(email, password);\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n }, []);\n\n const signUp = useCallback(\n async (email: string, password: string, displayName?: string) => {\n setError(null);\n try {\n return await signUpWithEmail(email, password, displayName);\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n },\n []\n );\n\n const signOut = useCallback(async () => {\n setError(null);\n try {\n await firebaseSignOut();\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n }, []);\n\n const signInWithGoogle = useCallback(async () => {\n setError(null);\n try {\n return await firebaseSignInWithGoogle();\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n }, []);\n\n const resetPassword = useCallback(async (email: string) => {\n setError(null);\n try {\n await firebaseResetPassword(email);\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n }, []);\n\n const updateProfile = useCallback(\n async (profile: { displayName?: string; photoURL?: string }) => {\n setError(null);\n if (!user) {\n const error = new Error('No authenticated user');\n setError(error);\n throw error;\n }\n try {\n await updateUserProfile(user, profile);\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n },\n [user]\n );\n\n const sendEmailVerificationFn = useCallback(async () => {\n setError(null);\n if (!user) {\n const error = new Error('No authenticated user');\n setError(error);\n throw error;\n }\n try {\n await sendVerificationEmail(user);\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n }, [user]);\n\n const clearError = useCallback(() => {\n setError(null);\n }, []);\n\n // Computed role-based properties\n const role = userData?.role ?? null;\n const isAdmin = role === 'owner' || role === 'admin';\n const isStaff = role === 'owner' || role === 'admin' || role === 'employee';\n const isOwner = role === 'owner';\n\n const value = useMemo<AuthContextValue>(\n () => ({\n user,\n userData,\n role,\n loading,\n userDataLoading,\n isAuthenticated: !!user,\n isAdmin,\n isStaff,\n isOwner,\n error,\n signIn,\n signUp,\n signOut,\n signInWithGoogle,\n resetPassword,\n updateProfile,\n sendEmailVerification: sendEmailVerificationFn,\n clearError,\n refreshUserData,\n }),\n [\n user,\n userData,\n role,\n loading,\n userDataLoading,\n isAdmin,\n isStaff,\n isOwner,\n error,\n signIn,\n signUp,\n signOut,\n signInWithGoogle,\n resetPassword,\n updateProfile,\n sendEmailVerificationFn,\n clearError,\n refreshUserData,\n ]\n );\n\n // Show loading component while determining auth state\n if (loading && loadingComponent) {\n return <>{loadingComponent}</>;\n }\n\n return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;\n}\n\n/**\n * Hook to access auth context\n * Must be used within an AuthProvider\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { user, signOut, isAuthenticated } = useAuthContext();\n * \n * if (!isAuthenticated) {\n * return <LoginForm />;\n * }\n * \n * return (\n * <div>\n * <p>Welcome, {user?.displayName}</p>\n * <button onClick={signOut}>Sign Out</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useAuthContext(): AuthContextValue {\n const context = useContext(AuthContext);\n \n if (context === undefined) {\n throw new Error('useAuthContext must be used within an AuthProvider');\n }\n \n return context;\n}\n\n/**\n * HOC for protecting routes that require authentication\n * Redirects to login or shows loading state\n */\nexport function withAuth<P extends object>(\n WrappedComponent: React.ComponentType<P>,\n options: {\n LoadingComponent?: React.ComponentType;\n UnauthenticatedComponent?: React.ComponentType;\n } = {}\n) {\n const { LoadingComponent, UnauthenticatedComponent } = options;\n\n return function WithAuthComponent(props: P) {\n const { user, loading, isAuthenticated } = useAuthContext();\n\n if (loading) {\n return LoadingComponent ? <LoadingComponent /> : <div>Loading...</div>;\n }\n\n if (!isAuthenticated) {\n return UnauthenticatedComponent ? (\n <UnauthenticatedComponent />\n ) : (\n <div>Please sign in to continue</div>\n );\n }\n\n return <WrappedComponent {...props} />;\n };\n}\n\n","'use client';\n\nimport React, { useEffect, ReactNode } from 'react';\nimport { useAuthContext } from './AuthContext';\n\nexport interface AdminRouteProps {\n children: ReactNode;\n /** URL to redirect non-admin users to (default: '/') */\n redirectTo?: string;\n /** Custom loading component */\n loadingComponent?: ReactNode;\n /** Custom access denied component (shown briefly before redirect) */\n accessDeniedComponent?: ReactNode;\n /** If true, also allows 'employee' role (uses isStaff instead of isAdmin) */\n allowStaff?: boolean;\n /** Callback when access is denied */\n onAccessDenied?: () => void;\n}\n\n/**\n * Wrapper component that protects routes for admin-only access\n * Redirects non-admin users to the specified path\n * \n * @example\n * ```tsx\n * // app/admin/page.tsx\n * import { AdminRoute } from '@marcwelti/mw-core';\n * \n * export default function AdminPage() {\n * return (\n * <AdminRoute redirectTo=\"/login\">\n * <AdminDashboard />\n * </AdminRoute>\n * );\n * }\n * ```\n */\nexport function AdminRoute({\n children,\n redirectTo = '/',\n loadingComponent,\n accessDeniedComponent,\n allowStaff = false,\n onAccessDenied,\n}: AdminRouteProps) {\n const { user, loading, userDataLoading, isAdmin, isStaff } = useAuthContext();\n\n const hasAccess = allowStaff ? isStaff : isAdmin;\n const isLoading = loading || userDataLoading;\n\n useEffect(() => {\n // Only redirect after loading is complete\n if (isLoading) return;\n\n // Not authenticated or no access\n if (!user || !hasAccess) {\n onAccessDenied?.();\n \n // Use window.location for redirect to work in all environments\n if (typeof window !== 'undefined') {\n window.location.href = redirectTo;\n }\n }\n }, [user, isLoading, hasAccess, redirectTo, onAccessDenied]);\n\n // Show loading state\n if (isLoading) {\n return loadingComponent ? (\n <>{loadingComponent}</>\n ) : (\n <div className=\"flex min-h-screen items-center justify-center\">\n <div className=\"h-8 w-8 animate-spin rounded-full border-4 border-primary border-t-transparent\" />\n </div>\n );\n }\n\n // Not authenticated or no access - show nothing or access denied component while redirecting\n if (!user || !hasAccess) {\n return accessDeniedComponent ? <>{accessDeniedComponent}</> : null;\n }\n\n // User has access\n return <>{children}</>;\n}\n\n/**\n * Wrapper component that protects routes for staff-only access (owner, admin, or employee)\n * Convenience wrapper around AdminRoute with allowStaff=true\n */\nexport function StaffRoute(props: Omit<AdminRouteProps, 'allowStaff'>) {\n return <AdminRoute {...props} allowStaff />;\n}\n\n/**\n * Wrapper component that protects routes for owner-only access\n */\nexport function OwnerRoute({\n children,\n redirectTo = '/',\n loadingComponent,\n accessDeniedComponent,\n onAccessDenied,\n}: Omit<AdminRouteProps, 'allowStaff'>) {\n const { user, loading, userDataLoading, isOwner } = useAuthContext();\n\n const isLoading = loading || userDataLoading;\n\n useEffect(() => {\n if (isLoading) return;\n\n if (!user || !isOwner) {\n onAccessDenied?.();\n \n if (typeof window !== 'undefined') {\n window.location.href = redirectTo;\n }\n }\n }, [user, isLoading, isOwner, redirectTo, onAccessDenied]);\n\n if (isLoading) {\n return loadingComponent ? (\n <>{loadingComponent}</>\n ) : (\n <div className=\"flex min-h-screen items-center justify-center\">\n <div className=\"h-8 w-8 animate-spin rounded-full border-4 border-primary border-t-transparent\" />\n </div>\n );\n }\n\n if (!user || !isOwner) {\n return accessDeniedComponent ? <>{accessDeniedComponent}</> : null;\n }\n\n return <>{children}</>;\n}\n\n"]}
|
|
@@ -42,6 +42,7 @@ declare function getFirebaseApp(): FirebaseApp;
|
|
|
42
42
|
declare function getFirebaseAuth(): Auth;
|
|
43
43
|
/**
|
|
44
44
|
* Get the Firestore database instance
|
|
45
|
+
* Supports named databases via NEXT_PUBLIC_FIRESTORE_DATABASE_ID env var
|
|
45
46
|
*/
|
|
46
47
|
declare function getFirebaseFirestore(): Firestore;
|
|
47
48
|
/**
|
package/dist/firebase/index.d.ts
CHANGED
|
@@ -42,6 +42,7 @@ declare function getFirebaseApp(): FirebaseApp;
|
|
|
42
42
|
declare function getFirebaseAuth(): Auth;
|
|
43
43
|
/**
|
|
44
44
|
* Get the Firestore database instance
|
|
45
|
+
* Supports named databases via NEXT_PUBLIC_FIRESTORE_DATABASE_ID env var
|
|
45
46
|
*/
|
|
46
47
|
declare function getFirebaseFirestore(): Firestore;
|
|
47
48
|
/**
|
package/dist/firebase/index.js
CHANGED
|
@@ -58,7 +58,12 @@ function getFirebaseAuth() {
|
|
|
58
58
|
}
|
|
59
59
|
function getFirebaseFirestore() {
|
|
60
60
|
if (!_db) {
|
|
61
|
-
|
|
61
|
+
const databaseId = process.env.NEXT_PUBLIC_FIRESTORE_DATABASE_ID;
|
|
62
|
+
if (databaseId) {
|
|
63
|
+
_db = firestore.getFirestore(getFirebaseApp(), databaseId);
|
|
64
|
+
} else {
|
|
65
|
+
_db = firestore.getFirestore(getFirebaseApp());
|
|
66
|
+
}
|
|
62
67
|
}
|
|
63
68
|
return _db;
|
|
64
69
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/firebase/config.ts","../../src/firebase/auth.ts","../../src/firebase/firestore.ts","../../src/firebase/storage.ts","../../src/firebase/analytics.ts"],"names":["getApps","getApp","initializeApp","getAuth","getFirestore","getStorage","auth","signInWithEmailAndPassword","createUserWithEmailAndPassword","updateProfile","firebaseSignOut","sendPasswordResetEmail","sendEmailVerification","GoogleAuthProvider","signInWithPopup","signInWithRedirect","getRedirectResult","onAuthStateChanged","db","doc","collection","getDoc","query","getDocs","addDoc","serverTimestamp","setDoc","updateDoc","deleteDoc","onSnapshot","storage","ref","uploadBytes","getDownloadURL","uploadBytesResumable","deleteObject","listAll","getMetadata","updateMetadata","isSupported","getAnalytics","analytics","logEvent"],"mappings":"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","file":"index.js","sourcesContent":["import { initializeApp, getApps, getApp, FirebaseApp } from 'firebase/app';\nimport { getAuth, Auth } from 'firebase/auth';\nimport { getFirestore, Firestore } from 'firebase/firestore';\nimport { getStorage, FirebaseStorage } from 'firebase/storage';\n\n/**\n * Firebase configuration interface\n * All values should be provided via environment variables\n */\nexport interface FirebaseConfig {\n apiKey: string;\n authDomain: string;\n projectId: string;\n storageBucket: string;\n messagingSenderId: string;\n appId: string;\n measurementId?: string;\n}\n\n/**\n * Get Firebase configuration from environment variables\n * Works with Next.js NEXT_PUBLIC_ prefix\n */\nexport function getFirebaseConfig(): FirebaseConfig {\n const config: FirebaseConfig = {\n apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY || '',\n authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN || '',\n projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID || '',\n storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET || '',\n messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID || '',\n appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID || '',\n measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID,\n };\n\n // Validate required fields\n const requiredFields: (keyof FirebaseConfig)[] = [\n 'apiKey',\n 'authDomain',\n 'projectId',\n 'storageBucket',\n 'messagingSenderId',\n 'appId',\n ];\n\n const missingFields = requiredFields.filter((field) => !config[field]);\n\n if (missingFields.length > 0) {\n console.warn(\n `[mw-core] Missing Firebase config fields: ${missingFields.join(', ')}. ` +\n `Make sure to set NEXT_PUBLIC_FIREBASE_* environment variables.`\n );\n }\n\n return config;\n}\n\n/**\n * Initialize Firebase app (singleton pattern)\n * Safe to call multiple times - returns existing instance if already initialized\n */\nexport function initializeFirebase(config?: FirebaseConfig): FirebaseApp {\n if (getApps().length > 0) {\n return getApp();\n }\n\n const firebaseConfig = config || getFirebaseConfig();\n return initializeApp(firebaseConfig);\n}\n\n// Lazy initialization singletons\nlet _app: FirebaseApp | null = null;\nlet _auth: Auth | null = null;\nlet _db: Firestore | null = null;\nlet _storage: FirebaseStorage | null = null;\n\n/**\n * Get the Firebase app instance\n */\nexport function getFirebaseApp(): FirebaseApp {\n if (!_app) {\n _app = initializeFirebase();\n }\n return _app;\n}\n\n/**\n * Get the Firebase Auth instance\n */\nexport function getFirebaseAuth(): Auth {\n if (!_auth) {\n _auth = getAuth(getFirebaseApp());\n }\n return _auth;\n}\n\n/**\n * Get the Firestore database instance\n */\nexport function getFirebaseFirestore(): Firestore {\n if (!_db) {\n _db = getFirestore(getFirebaseApp());\n }\n return _db;\n}\n\n/**\n * Get the Firebase Storage instance\n */\nexport function getFirebaseStorage(): FirebaseStorage {\n if (!_storage) {\n _storage = getStorage(getFirebaseApp());\n }\n return _storage;\n}\n\n// Convenience exports for direct access\nexport const app = typeof window !== 'undefined' ? getFirebaseApp() : null;\nexport const auth = typeof window !== 'undefined' ? getFirebaseAuth() : null;\nexport const db = typeof window !== 'undefined' ? getFirebaseFirestore() : null;\nexport const storage = typeof window !== 'undefined' ? getFirebaseStorage() : null;\n\n","import {\n signInWithEmailAndPassword,\n createUserWithEmailAndPassword,\n signOut as firebaseSignOut,\n sendPasswordResetEmail,\n sendEmailVerification,\n updateProfile,\n GoogleAuthProvider,\n signInWithPopup,\n signInWithRedirect,\n getRedirectResult,\n onAuthStateChanged,\n User,\n UserCredential,\n Auth,\n} from 'firebase/auth';\nimport { getFirebaseAuth } from './config';\n\n/**\n * Sign in with email and password\n */\nexport async function signInWithEmail(\n email: string,\n password: string\n): Promise<UserCredential> {\n const auth = getFirebaseAuth();\n return signInWithEmailAndPassword(auth, email, password);\n}\n\n/**\n * Create a new account with email and password\n */\nexport async function signUpWithEmail(\n email: string,\n password: string,\n displayName?: string\n): Promise<UserCredential> {\n const auth = getFirebaseAuth();\n const credential = await createUserWithEmailAndPassword(auth, email, password);\n \n if (displayName && credential.user) {\n await updateProfile(credential.user, { displayName });\n }\n \n return credential;\n}\n\n/**\n * Sign out the current user\n */\nexport async function signOut(): Promise<void> {\n const auth = getFirebaseAuth();\n return firebaseSignOut(auth);\n}\n\n/**\n * Send a password reset email\n */\nexport async function resetPassword(email: string): Promise<void> {\n const auth = getFirebaseAuth();\n return sendPasswordResetEmail(auth, email);\n}\n\n/**\n * Send email verification to the current user\n */\nexport async function sendVerificationEmail(user: User): Promise<void> {\n return sendEmailVerification(user);\n}\n\n/**\n * Update the current user's profile\n */\nexport async function updateUserProfile(\n user: User,\n profile: { displayName?: string; photoURL?: string }\n): Promise<void> {\n return updateProfile(user, profile);\n}\n\n/**\n * Sign in with Google using popup\n */\nexport async function signInWithGoogle(): Promise<UserCredential> {\n const auth = getFirebaseAuth();\n const provider = new GoogleAuthProvider();\n provider.addScope('email');\n provider.addScope('profile');\n return signInWithPopup(auth, provider);\n}\n\n/**\n * Sign in with Google using redirect (better for mobile)\n */\nexport async function signInWithGoogleRedirect(): Promise<void> {\n const auth = getFirebaseAuth();\n const provider = new GoogleAuthProvider();\n provider.addScope('email');\n provider.addScope('profile');\n return signInWithRedirect(auth, provider);\n}\n\n/**\n * Get the result of a redirect sign-in\n */\nexport async function getGoogleRedirectResult(): Promise<UserCredential | null> {\n const auth = getFirebaseAuth();\n return getRedirectResult(auth);\n}\n\n/**\n * Subscribe to auth state changes\n */\nexport function subscribeToAuthState(\n callback: (user: User | null) => void\n): () => void {\n const auth = getFirebaseAuth();\n return onAuthStateChanged(auth, callback);\n}\n\n/**\n * Get the current user (may be null if not authenticated)\n */\nexport function getCurrentUser(): User | null {\n const auth = getFirebaseAuth();\n return auth.currentUser;\n}\n\n/**\n * Wait for the auth state to be determined\n * Useful for SSR/initial load\n */\nexport function waitForAuthState(): Promise<User | null> {\n return new Promise((resolve) => {\n const auth = getFirebaseAuth();\n const unsubscribe = onAuthStateChanged(auth, (user) => {\n unsubscribe();\n resolve(user);\n });\n });\n}\n\n// Re-export useful types\nexport type { User, UserCredential, Auth };\n\n","import {\n collection,\n doc,\n getDoc,\n getDocs,\n setDoc,\n updateDoc,\n deleteDoc,\n addDoc,\n query,\n where,\n orderBy,\n limit,\n startAfter,\n onSnapshot,\n DocumentData,\n DocumentReference,\n CollectionReference,\n QueryConstraint,\n DocumentSnapshot,\n QuerySnapshot,\n Timestamp,\n serverTimestamp,\n FieldValue,\n WhereFilterOp,\n OrderByDirection,\n Unsubscribe,\n} from 'firebase/firestore';\nimport { getFirebaseFirestore } from './config';\n\n/**\n * Get a document reference\n */\nexport function getDocRef<T = DocumentData>(\n collectionPath: string,\n documentId: string\n): DocumentReference<T> {\n const db = getFirebaseFirestore();\n return doc(db, collectionPath, documentId) as DocumentReference<T>;\n}\n\n/**\n * Get a collection reference\n */\nexport function getCollectionRef<T = DocumentData>(\n collectionPath: string\n): CollectionReference<T> {\n const db = getFirebaseFirestore();\n return collection(db, collectionPath) as CollectionReference<T>;\n}\n\n/**\n * Get a single document by ID\n */\nexport async function getDocument<T = DocumentData>(\n collectionPath: string,\n documentId: string\n): Promise<T | null> {\n const docRef = getDocRef<T>(collectionPath, documentId);\n const docSnap = await getDoc(docRef);\n \n if (docSnap.exists()) {\n return { id: docSnap.id, ...docSnap.data() } as T;\n }\n \n return null;\n}\n\n/**\n * Get all documents in a collection\n */\nexport async function getCollection<T = DocumentData>(\n collectionPath: string,\n constraints: QueryConstraint[] = []\n): Promise<T[]> {\n const collectionRef = getCollectionRef<T>(collectionPath);\n const q = query(collectionRef, ...constraints);\n const querySnapshot = await getDocs(q);\n \n return querySnapshot.docs.map((doc) => ({\n id: doc.id,\n ...doc.data(),\n })) as T[];\n}\n\n/**\n * Add a new document with auto-generated ID\n */\nexport async function addDocument<T = DocumentData>(\n collectionPath: string,\n data: Omit<T, 'id'>\n): Promise<string> {\n const collectionRef = getCollectionRef(collectionPath);\n const docRef = await addDoc(collectionRef, {\n ...data,\n createdAt: serverTimestamp(),\n updatedAt: serverTimestamp(),\n });\n return docRef.id;\n}\n\n/**\n * Set/create a document with a specific ID\n */\nexport async function setDocument<T = DocumentData>(\n collectionPath: string,\n documentId: string,\n data: Omit<T, 'id'>,\n merge: boolean = false\n): Promise<void> {\n const docRef = getDocRef(collectionPath, documentId);\n await setDoc(\n docRef,\n {\n ...data,\n updatedAt: serverTimestamp(),\n ...(merge ? {} : { createdAt: serverTimestamp() }),\n },\n { merge }\n );\n}\n\n/**\n * Update specific fields in a document\n */\nexport async function updateDocument<T = DocumentData>(\n collectionPath: string,\n documentId: string,\n data: Partial<T>\n): Promise<void> {\n const docRef = getDocRef(collectionPath, documentId);\n await updateDoc(docRef, {\n ...data,\n updatedAt: serverTimestamp(),\n });\n}\n\n/**\n * Delete a document\n */\nexport async function deleteDocument(\n collectionPath: string,\n documentId: string\n): Promise<void> {\n const docRef = getDocRef(collectionPath, documentId);\n await deleteDoc(docRef);\n}\n\n/**\n * Subscribe to a single document\n */\nexport function subscribeToDocument<T = DocumentData>(\n collectionPath: string,\n documentId: string,\n callback: (data: T | null) => void\n): Unsubscribe {\n const docRef = getDocRef<T>(collectionPath, documentId);\n \n return onSnapshot(docRef, (docSnap) => {\n if (docSnap.exists()) {\n callback({ id: docSnap.id, ...docSnap.data() } as T);\n } else {\n callback(null);\n }\n });\n}\n\n/**\n * Subscribe to a collection\n */\nexport function subscribeToCollection<T = DocumentData>(\n collectionPath: string,\n callback: (data: T[]) => void,\n constraints: QueryConstraint[] = []\n): Unsubscribe {\n const collectionRef = getCollectionRef<T>(collectionPath);\n const q = query(collectionRef, ...constraints);\n \n return onSnapshot(q, (querySnapshot) => {\n const data = querySnapshot.docs.map((doc) => ({\n id: doc.id,\n ...doc.data(),\n })) as T[];\n callback(data);\n });\n}\n\n// Export query helpers for convenience\nexport {\n query,\n where,\n orderBy,\n limit,\n startAfter,\n Timestamp,\n serverTimestamp,\n};\n\n// Re-export types\nexport type {\n DocumentData,\n DocumentReference,\n CollectionReference,\n QueryConstraint,\n DocumentSnapshot,\n QuerySnapshot,\n FieldValue,\n WhereFilterOp,\n OrderByDirection,\n Unsubscribe,\n};\n\n","import {\n ref,\n uploadBytes,\n uploadBytesResumable,\n getDownloadURL,\n deleteObject,\n listAll,\n getMetadata,\n updateMetadata,\n StorageReference,\n UploadTask,\n UploadMetadata,\n FullMetadata,\n ListResult,\n} from 'firebase/storage';\nimport { getFirebaseStorage } from './config';\n\n/**\n * Get a reference to a storage location\n */\nexport function getStorageRef(path: string): StorageReference {\n const storage = getFirebaseStorage();\n return ref(storage, path);\n}\n\n/**\n * Upload a file to storage\n */\nexport async function uploadFile(\n path: string,\n file: Blob | Uint8Array | ArrayBuffer,\n metadata?: UploadMetadata\n): Promise<string> {\n const storageRef = getStorageRef(path);\n await uploadBytes(storageRef, file, metadata);\n return getDownloadURL(storageRef);\n}\n\n/**\n * Upload a file with progress tracking\n */\nexport function uploadFileWithProgress(\n path: string,\n file: Blob | Uint8Array | ArrayBuffer,\n onProgress?: (progress: number) => void,\n metadata?: UploadMetadata\n): { task: UploadTask; promise: Promise<string> } {\n const storageRef = getStorageRef(path);\n const task = uploadBytesResumable(storageRef, file, metadata);\n \n if (onProgress) {\n task.on('state_changed', (snapshot) => {\n const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;\n onProgress(progress);\n });\n }\n \n const promise = new Promise<string>((resolve, reject) => {\n task.on(\n 'state_changed',\n null,\n (error) => reject(error),\n async () => {\n const url = await getDownloadURL(task.snapshot.ref);\n resolve(url);\n }\n );\n });\n \n return { task, promise };\n}\n\n/**\n * Get the download URL for a file\n */\nexport async function getFileURL(path: string): Promise<string> {\n const storageRef = getStorageRef(path);\n return getDownloadURL(storageRef);\n}\n\n/**\n * Delete a file from storage\n */\nexport async function deleteFile(path: string): Promise<void> {\n const storageRef = getStorageRef(path);\n return deleteObject(storageRef);\n}\n\n/**\n * List all files in a directory\n */\nexport async function listFiles(path: string): Promise<ListResult> {\n const storageRef = getStorageRef(path);\n return listAll(storageRef);\n}\n\n/**\n * Get metadata for a file\n */\nexport async function getFileMetadata(path: string): Promise<FullMetadata> {\n const storageRef = getStorageRef(path);\n return getMetadata(storageRef);\n}\n\n/**\n * Update metadata for a file\n */\nexport async function updateFileMetadata(\n path: string,\n metadata: Partial<UploadMetadata>\n): Promise<FullMetadata> {\n const storageRef = getStorageRef(path);\n return updateMetadata(storageRef, metadata);\n}\n\n/**\n * Generate a unique file path with timestamp\n */\nexport function generateFilePath(\n folder: string,\n filename: string,\n userId?: string\n): string {\n const timestamp = Date.now();\n const extension = filename.split('.').pop() || '';\n const baseName = filename.replace(/\\.[^/.]+$/, '');\n const sanitizedName = baseName.replace(/[^a-zA-Z0-9]/g, '_');\n \n if (userId) {\n return `${folder}/${userId}/${timestamp}_${sanitizedName}.${extension}`;\n }\n \n return `${folder}/${timestamp}_${sanitizedName}.${extension}`;\n}\n\n// Re-export types\nexport type {\n StorageReference,\n UploadTask,\n UploadMetadata,\n FullMetadata,\n ListResult,\n};\n\n","import { getAnalytics, logEvent, Analytics, isSupported } from 'firebase/analytics';\nimport { getFirebaseApp } from './config';\n\nlet _analytics: Analytics | null = null;\n\n/**\n * Get the Firebase Analytics instance\n * Only works in browser environment\n */\nexport async function getFirebaseAnalytics(): Promise<Analytics | null> {\n if (typeof window === 'undefined') {\n return null;\n }\n\n if (_analytics) {\n return _analytics;\n }\n\n const supported = await isSupported();\n if (!supported) {\n console.warn('[mw-core] Firebase Analytics is not supported in this environment');\n return null;\n }\n\n _analytics = getAnalytics(getFirebaseApp());\n return _analytics;\n}\n\n/**\n * Log a custom event to Firebase Analytics\n */\nexport async function trackEvent(\n eventName: string,\n eventParams?: Record<string, unknown>\n): Promise<void> {\n const analytics = await getFirebaseAnalytics();\n if (analytics) {\n logEvent(analytics, eventName, eventParams);\n }\n}\n\n/**\n * Track a page view\n */\nexport async function trackPageView(\n pagePath: string,\n pageTitle?: string\n): Promise<void> {\n await trackEvent('page_view', {\n page_path: pagePath,\n page_title: pageTitle,\n });\n}\n\n/**\n * Track a user action\n */\nexport async function trackUserAction(\n action: string,\n category: string,\n label?: string,\n value?: number\n): Promise<void> {\n await trackEvent(action, {\n event_category: category,\n event_label: label,\n value,\n });\n}\n\n/**\n * Track sign up event\n */\nexport async function trackSignUp(method: string): Promise<void> {\n await trackEvent('sign_up', { method });\n}\n\n/**\n * Track login event\n */\nexport async function trackLogin(method: string): Promise<void> {\n await trackEvent('login', { method });\n}\n\n// Re-export types\nexport type { Analytics };\n\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/firebase/config.ts","../../src/firebase/auth.ts","../../src/firebase/firestore.ts","../../src/firebase/storage.ts","../../src/firebase/analytics.ts"],"names":["getApps","getApp","initializeApp","getAuth","getFirestore","getStorage","auth","signInWithEmailAndPassword","createUserWithEmailAndPassword","updateProfile","firebaseSignOut","sendPasswordResetEmail","sendEmailVerification","GoogleAuthProvider","signInWithPopup","signInWithRedirect","getRedirectResult","onAuthStateChanged","db","doc","collection","getDoc","query","getDocs","addDoc","serverTimestamp","setDoc","updateDoc","deleteDoc","onSnapshot","storage","ref","uploadBytes","getDownloadURL","uploadBytesResumable","deleteObject","listAll","getMetadata","updateMetadata","isSupported","getAnalytics","analytics","logEvent"],"mappings":"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","file":"index.js","sourcesContent":["import { initializeApp, getApps, getApp, FirebaseApp } from 'firebase/app';\nimport { getAuth, Auth } from 'firebase/auth';\nimport { getFirestore, Firestore } from 'firebase/firestore';\nimport { getStorage, FirebaseStorage } from 'firebase/storage';\n\n/**\n * Firebase configuration interface\n * All values should be provided via environment variables\n */\nexport interface FirebaseConfig {\n apiKey: string;\n authDomain: string;\n projectId: string;\n storageBucket: string;\n messagingSenderId: string;\n appId: string;\n measurementId?: string;\n}\n\n/**\n * Get Firebase configuration from environment variables\n * Works with Next.js NEXT_PUBLIC_ prefix\n */\nexport function getFirebaseConfig(): FirebaseConfig {\n const config: FirebaseConfig = {\n apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY || '',\n authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN || '',\n projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID || '',\n storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET || '',\n messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID || '',\n appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID || '',\n measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID,\n };\n\n // Validate required fields\n const requiredFields: (keyof FirebaseConfig)[] = [\n 'apiKey',\n 'authDomain',\n 'projectId',\n 'storageBucket',\n 'messagingSenderId',\n 'appId',\n ];\n\n const missingFields = requiredFields.filter((field) => !config[field]);\n\n if (missingFields.length > 0) {\n console.warn(\n `[mw-core] Missing Firebase config fields: ${missingFields.join(', ')}. ` +\n `Make sure to set NEXT_PUBLIC_FIREBASE_* environment variables.`\n );\n }\n\n return config;\n}\n\n/**\n * Initialize Firebase app (singleton pattern)\n * Safe to call multiple times - returns existing instance if already initialized\n */\nexport function initializeFirebase(config?: FirebaseConfig): FirebaseApp {\n if (getApps().length > 0) {\n return getApp();\n }\n\n const firebaseConfig = config || getFirebaseConfig();\n return initializeApp(firebaseConfig);\n}\n\n// Lazy initialization singletons\nlet _app: FirebaseApp | null = null;\nlet _auth: Auth | null = null;\nlet _db: Firestore | null = null;\nlet _storage: FirebaseStorage | null = null;\n\n/**\n * Get the Firebase app instance\n */\nexport function getFirebaseApp(): FirebaseApp {\n if (!_app) {\n _app = initializeFirebase();\n }\n return _app;\n}\n\n/**\n * Get the Firebase Auth instance\n */\nexport function getFirebaseAuth(): Auth {\n if (!_auth) {\n _auth = getAuth(getFirebaseApp());\n }\n return _auth;\n}\n\n/**\n * Get the Firestore database instance\n * Supports named databases via NEXT_PUBLIC_FIRESTORE_DATABASE_ID env var\n */\nexport function getFirebaseFirestore(): Firestore {\n if (!_db) {\n const databaseId = process.env.NEXT_PUBLIC_FIRESTORE_DATABASE_ID;\n if (databaseId) {\n _db = getFirestore(getFirebaseApp(), databaseId);\n } else {\n _db = getFirestore(getFirebaseApp());\n }\n }\n return _db;\n}\n\n/**\n * Get the Firebase Storage instance\n */\nexport function getFirebaseStorage(): FirebaseStorage {\n if (!_storage) {\n _storage = getStorage(getFirebaseApp());\n }\n return _storage;\n}\n\n// Convenience exports for direct access\nexport const app = typeof window !== 'undefined' ? getFirebaseApp() : null;\nexport const auth = typeof window !== 'undefined' ? getFirebaseAuth() : null;\nexport const db = typeof window !== 'undefined' ? getFirebaseFirestore() : null;\nexport const storage = typeof window !== 'undefined' ? getFirebaseStorage() : null;\n\n","import {\n signInWithEmailAndPassword,\n createUserWithEmailAndPassword,\n signOut as firebaseSignOut,\n sendPasswordResetEmail,\n sendEmailVerification,\n updateProfile,\n GoogleAuthProvider,\n signInWithPopup,\n signInWithRedirect,\n getRedirectResult,\n onAuthStateChanged,\n User,\n UserCredential,\n Auth,\n} from 'firebase/auth';\nimport { getFirebaseAuth } from './config';\n\n/**\n * Sign in with email and password\n */\nexport async function signInWithEmail(\n email: string,\n password: string\n): Promise<UserCredential> {\n const auth = getFirebaseAuth();\n return signInWithEmailAndPassword(auth, email, password);\n}\n\n/**\n * Create a new account with email and password\n */\nexport async function signUpWithEmail(\n email: string,\n password: string,\n displayName?: string\n): Promise<UserCredential> {\n const auth = getFirebaseAuth();\n const credential = await createUserWithEmailAndPassword(auth, email, password);\n \n if (displayName && credential.user) {\n await updateProfile(credential.user, { displayName });\n }\n \n return credential;\n}\n\n/**\n * Sign out the current user\n */\nexport async function signOut(): Promise<void> {\n const auth = getFirebaseAuth();\n return firebaseSignOut(auth);\n}\n\n/**\n * Send a password reset email\n */\nexport async function resetPassword(email: string): Promise<void> {\n const auth = getFirebaseAuth();\n return sendPasswordResetEmail(auth, email);\n}\n\n/**\n * Send email verification to the current user\n */\nexport async function sendVerificationEmail(user: User): Promise<void> {\n return sendEmailVerification(user);\n}\n\n/**\n * Update the current user's profile\n */\nexport async function updateUserProfile(\n user: User,\n profile: { displayName?: string; photoURL?: string }\n): Promise<void> {\n return updateProfile(user, profile);\n}\n\n/**\n * Sign in with Google using popup\n */\nexport async function signInWithGoogle(): Promise<UserCredential> {\n const auth = getFirebaseAuth();\n const provider = new GoogleAuthProvider();\n provider.addScope('email');\n provider.addScope('profile');\n return signInWithPopup(auth, provider);\n}\n\n/**\n * Sign in with Google using redirect (better for mobile)\n */\nexport async function signInWithGoogleRedirect(): Promise<void> {\n const auth = getFirebaseAuth();\n const provider = new GoogleAuthProvider();\n provider.addScope('email');\n provider.addScope('profile');\n return signInWithRedirect(auth, provider);\n}\n\n/**\n * Get the result of a redirect sign-in\n */\nexport async function getGoogleRedirectResult(): Promise<UserCredential | null> {\n const auth = getFirebaseAuth();\n return getRedirectResult(auth);\n}\n\n/**\n * Subscribe to auth state changes\n */\nexport function subscribeToAuthState(\n callback: (user: User | null) => void\n): () => void {\n const auth = getFirebaseAuth();\n return onAuthStateChanged(auth, callback);\n}\n\n/**\n * Get the current user (may be null if not authenticated)\n */\nexport function getCurrentUser(): User | null {\n const auth = getFirebaseAuth();\n return auth.currentUser;\n}\n\n/**\n * Wait for the auth state to be determined\n * Useful for SSR/initial load\n */\nexport function waitForAuthState(): Promise<User | null> {\n return new Promise((resolve) => {\n const auth = getFirebaseAuth();\n const unsubscribe = onAuthStateChanged(auth, (user) => {\n unsubscribe();\n resolve(user);\n });\n });\n}\n\n// Re-export useful types\nexport type { User, UserCredential, Auth };\n\n","import {\n collection,\n doc,\n getDoc,\n getDocs,\n setDoc,\n updateDoc,\n deleteDoc,\n addDoc,\n query,\n where,\n orderBy,\n limit,\n startAfter,\n onSnapshot,\n DocumentData,\n DocumentReference,\n CollectionReference,\n QueryConstraint,\n DocumentSnapshot,\n QuerySnapshot,\n Timestamp,\n serverTimestamp,\n FieldValue,\n WhereFilterOp,\n OrderByDirection,\n Unsubscribe,\n} from 'firebase/firestore';\nimport { getFirebaseFirestore } from './config';\n\n/**\n * Get a document reference\n */\nexport function getDocRef<T = DocumentData>(\n collectionPath: string,\n documentId: string\n): DocumentReference<T> {\n const db = getFirebaseFirestore();\n return doc(db, collectionPath, documentId) as DocumentReference<T>;\n}\n\n/**\n * Get a collection reference\n */\nexport function getCollectionRef<T = DocumentData>(\n collectionPath: string\n): CollectionReference<T> {\n const db = getFirebaseFirestore();\n return collection(db, collectionPath) as CollectionReference<T>;\n}\n\n/**\n * Get a single document by ID\n */\nexport async function getDocument<T = DocumentData>(\n collectionPath: string,\n documentId: string\n): Promise<T | null> {\n const docRef = getDocRef<T>(collectionPath, documentId);\n const docSnap = await getDoc(docRef);\n \n if (docSnap.exists()) {\n return { id: docSnap.id, ...docSnap.data() } as T;\n }\n \n return null;\n}\n\n/**\n * Get all documents in a collection\n */\nexport async function getCollection<T = DocumentData>(\n collectionPath: string,\n constraints: QueryConstraint[] = []\n): Promise<T[]> {\n const collectionRef = getCollectionRef<T>(collectionPath);\n const q = query(collectionRef, ...constraints);\n const querySnapshot = await getDocs(q);\n \n return querySnapshot.docs.map((doc) => ({\n id: doc.id,\n ...doc.data(),\n })) as T[];\n}\n\n/**\n * Add a new document with auto-generated ID\n */\nexport async function addDocument<T = DocumentData>(\n collectionPath: string,\n data: Omit<T, 'id'>\n): Promise<string> {\n const collectionRef = getCollectionRef(collectionPath);\n const docRef = await addDoc(collectionRef, {\n ...data,\n createdAt: serverTimestamp(),\n updatedAt: serverTimestamp(),\n });\n return docRef.id;\n}\n\n/**\n * Set/create a document with a specific ID\n */\nexport async function setDocument<T = DocumentData>(\n collectionPath: string,\n documentId: string,\n data: Omit<T, 'id'>,\n merge: boolean = false\n): Promise<void> {\n const docRef = getDocRef(collectionPath, documentId);\n await setDoc(\n docRef,\n {\n ...data,\n updatedAt: serverTimestamp(),\n ...(merge ? {} : { createdAt: serverTimestamp() }),\n },\n { merge }\n );\n}\n\n/**\n * Update specific fields in a document\n */\nexport async function updateDocument<T = DocumentData>(\n collectionPath: string,\n documentId: string,\n data: Partial<T>\n): Promise<void> {\n const docRef = getDocRef(collectionPath, documentId);\n await updateDoc(docRef, {\n ...data,\n updatedAt: serverTimestamp(),\n });\n}\n\n/**\n * Delete a document\n */\nexport async function deleteDocument(\n collectionPath: string,\n documentId: string\n): Promise<void> {\n const docRef = getDocRef(collectionPath, documentId);\n await deleteDoc(docRef);\n}\n\n/**\n * Subscribe to a single document\n */\nexport function subscribeToDocument<T = DocumentData>(\n collectionPath: string,\n documentId: string,\n callback: (data: T | null) => void\n): Unsubscribe {\n const docRef = getDocRef<T>(collectionPath, documentId);\n \n return onSnapshot(docRef, (docSnap) => {\n if (docSnap.exists()) {\n callback({ id: docSnap.id, ...docSnap.data() } as T);\n } else {\n callback(null);\n }\n });\n}\n\n/**\n * Subscribe to a collection\n */\nexport function subscribeToCollection<T = DocumentData>(\n collectionPath: string,\n callback: (data: T[]) => void,\n constraints: QueryConstraint[] = []\n): Unsubscribe {\n const collectionRef = getCollectionRef<T>(collectionPath);\n const q = query(collectionRef, ...constraints);\n \n return onSnapshot(q, (querySnapshot) => {\n const data = querySnapshot.docs.map((doc) => ({\n id: doc.id,\n ...doc.data(),\n })) as T[];\n callback(data);\n });\n}\n\n// Export query helpers for convenience\nexport {\n query,\n where,\n orderBy,\n limit,\n startAfter,\n Timestamp,\n serverTimestamp,\n};\n\n// Re-export types\nexport type {\n DocumentData,\n DocumentReference,\n CollectionReference,\n QueryConstraint,\n DocumentSnapshot,\n QuerySnapshot,\n FieldValue,\n WhereFilterOp,\n OrderByDirection,\n Unsubscribe,\n};\n\n","import {\n ref,\n uploadBytes,\n uploadBytesResumable,\n getDownloadURL,\n deleteObject,\n listAll,\n getMetadata,\n updateMetadata,\n StorageReference,\n UploadTask,\n UploadMetadata,\n FullMetadata,\n ListResult,\n} from 'firebase/storage';\nimport { getFirebaseStorage } from './config';\n\n/**\n * Get a reference to a storage location\n */\nexport function getStorageRef(path: string): StorageReference {\n const storage = getFirebaseStorage();\n return ref(storage, path);\n}\n\n/**\n * Upload a file to storage\n */\nexport async function uploadFile(\n path: string,\n file: Blob | Uint8Array | ArrayBuffer,\n metadata?: UploadMetadata\n): Promise<string> {\n const storageRef = getStorageRef(path);\n await uploadBytes(storageRef, file, metadata);\n return getDownloadURL(storageRef);\n}\n\n/**\n * Upload a file with progress tracking\n */\nexport function uploadFileWithProgress(\n path: string,\n file: Blob | Uint8Array | ArrayBuffer,\n onProgress?: (progress: number) => void,\n metadata?: UploadMetadata\n): { task: UploadTask; promise: Promise<string> } {\n const storageRef = getStorageRef(path);\n const task = uploadBytesResumable(storageRef, file, metadata);\n \n if (onProgress) {\n task.on('state_changed', (snapshot) => {\n const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;\n onProgress(progress);\n });\n }\n \n const promise = new Promise<string>((resolve, reject) => {\n task.on(\n 'state_changed',\n null,\n (error) => reject(error),\n async () => {\n const url = await getDownloadURL(task.snapshot.ref);\n resolve(url);\n }\n );\n });\n \n return { task, promise };\n}\n\n/**\n * Get the download URL for a file\n */\nexport async function getFileURL(path: string): Promise<string> {\n const storageRef = getStorageRef(path);\n return getDownloadURL(storageRef);\n}\n\n/**\n * Delete a file from storage\n */\nexport async function deleteFile(path: string): Promise<void> {\n const storageRef = getStorageRef(path);\n return deleteObject(storageRef);\n}\n\n/**\n * List all files in a directory\n */\nexport async function listFiles(path: string): Promise<ListResult> {\n const storageRef = getStorageRef(path);\n return listAll(storageRef);\n}\n\n/**\n * Get metadata for a file\n */\nexport async function getFileMetadata(path: string): Promise<FullMetadata> {\n const storageRef = getStorageRef(path);\n return getMetadata(storageRef);\n}\n\n/**\n * Update metadata for a file\n */\nexport async function updateFileMetadata(\n path: string,\n metadata: Partial<UploadMetadata>\n): Promise<FullMetadata> {\n const storageRef = getStorageRef(path);\n return updateMetadata(storageRef, metadata);\n}\n\n/**\n * Generate a unique file path with timestamp\n */\nexport function generateFilePath(\n folder: string,\n filename: string,\n userId?: string\n): string {\n const timestamp = Date.now();\n const extension = filename.split('.').pop() || '';\n const baseName = filename.replace(/\\.[^/.]+$/, '');\n const sanitizedName = baseName.replace(/[^a-zA-Z0-9]/g, '_');\n \n if (userId) {\n return `${folder}/${userId}/${timestamp}_${sanitizedName}.${extension}`;\n }\n \n return `${folder}/${timestamp}_${sanitizedName}.${extension}`;\n}\n\n// Re-export types\nexport type {\n StorageReference,\n UploadTask,\n UploadMetadata,\n FullMetadata,\n ListResult,\n};\n\n","import { getAnalytics, logEvent, Analytics, isSupported } from 'firebase/analytics';\nimport { getFirebaseApp } from './config';\n\nlet _analytics: Analytics | null = null;\n\n/**\n * Get the Firebase Analytics instance\n * Only works in browser environment\n */\nexport async function getFirebaseAnalytics(): Promise<Analytics | null> {\n if (typeof window === 'undefined') {\n return null;\n }\n\n if (_analytics) {\n return _analytics;\n }\n\n const supported = await isSupported();\n if (!supported) {\n console.warn('[mw-core] Firebase Analytics is not supported in this environment');\n return null;\n }\n\n _analytics = getAnalytics(getFirebaseApp());\n return _analytics;\n}\n\n/**\n * Log a custom event to Firebase Analytics\n */\nexport async function trackEvent(\n eventName: string,\n eventParams?: Record<string, unknown>\n): Promise<void> {\n const analytics = await getFirebaseAnalytics();\n if (analytics) {\n logEvent(analytics, eventName, eventParams);\n }\n}\n\n/**\n * Track a page view\n */\nexport async function trackPageView(\n pagePath: string,\n pageTitle?: string\n): Promise<void> {\n await trackEvent('page_view', {\n page_path: pagePath,\n page_title: pageTitle,\n });\n}\n\n/**\n * Track a user action\n */\nexport async function trackUserAction(\n action: string,\n category: string,\n label?: string,\n value?: number\n): Promise<void> {\n await trackEvent(action, {\n event_category: category,\n event_label: label,\n value,\n });\n}\n\n/**\n * Track sign up event\n */\nexport async function trackSignUp(method: string): Promise<void> {\n await trackEvent('sign_up', { method });\n}\n\n/**\n * Track login event\n */\nexport async function trackLogin(method: string): Promise<void> {\n await trackEvent('login', { method });\n}\n\n// Re-export types\nexport type { Analytics };\n\n"]}
|