This is my firebasconfig.ts
import { initializeApp } from 'firebase/app';
import { Firestore, getFirestore } from "firebase/firestore";
import { getStorage, FirebaseStorage } from "firebase/storage";
import { getDatabase } from "firebase/database";
const firebaseConfig = {
apiKey:"AIzaSyBWyddgiRluIqPN7jiQahT8mxxTrshUrYwA",
authDomain:"song-stories.firebaseapp.com",
// databaseURL:"https://music-ca8-default-rtdb.firebaseio.com",
projectId:"song-stories",
storageBucket:"music-stories.appspot.com",
messagingSenderId:"255650957734",
appId:"1:255650957734:web:fdg5838844a59bc20aba767",
// measurementId:"G-GJ4D5jPPX"
}
const app = initializeApp(firebaseConfig);
const storage: FirebaseStorage = getStorage(app);
const db = getFirestore(app);
// const db = getDatabase(app);
export {app, storage, db};
And this is my MostPopular React Component where i display Most popular category books
import { Grid, Paper, Typography, IconButton, Rating, Button } from '@mui/material';
import { Headset, Star } from '@mui/icons-material';
import { Product } from '../../app/models/product';
import {useNavigate, useLocation} from 'react-router-dom';
import StarRating from './StarRating';
import RatingPopup from './RatingPopup';
import { db } from '../../../.config/firebase.config.ts';
import { doc, getDoc, updateDoc } from 'firebase/firestore/lite';
import 'firebase/firestore';
import { collection, getDocs } from 'firebase/firestore/lite';
import { query, where } from 'firebase/firestore';
interface MostPopularCardProps {
products: Product[];
onBookClick: (title: string) => void;
fetchAllMostPopular: (products: Product[]) => Product[];
}
interface ProductDoc {
ratings: number[]
}
const MostPopular: React.FC<MostPopularCardProps> = ({ products, onBookClick, fetchAllMostPopular }) => {
const [loading, setLoading] = useState(false);
const [selectedProduct, setSelectedProduct] = useState<Product | null>(null);
const [openRatingPopup, setOpenRatingPopup] = useState(false);
const [averageRatings, setAverageRatings] = useState<Record<string, number>>({});
const navigate = useNavigate();
const location = useLocation();
const handleFetchAll = () => {
navigate(location.state?.from || '/seeall')
};
const handleOpenRatingPopup = (product: Product) => {
setSelectedProduct(product);
setOpenRatingPopup(true);
}
const handleCloseRatingPopup = () => {
setOpenRatingPopup(false);
}
// const db = getDatabase();
async function handleSaveRating(firestoreRef: any, selectedProduct: Product, rating: number) {
try {
const productRef = doc(collection(firestoreRef, `rating_book/${selectedProduct?.bk_ID}`));
const productSnap = await getDoc(productRef);
if (productSnap.exists()) {
// Get the data from the snapshot
const productData = productSnap.data() as ProductDoc;
// Calculate new ratings
const ratings = productData.ratings || [];
const newRatings = [...ratings, rating];
// Calculate total and average rating
const totalRating = newRatings.reduce((acc, curr) => acc + curr, 0);
const averageRating = totalRating / newRatings.length;
// Update the product document with new ratings
await updateDoc(productRef, { ratings: newRatings, averageRating });
// Log success message and close rating popup
console.log(`Saving rating ${rating} for product ${selectedProduct?.bk_ID}`);
handleCloseRatingPopup();
} else {
console.error("Document does not exist");
}
}catch(error: any){
console.error("Error saving rating", error.message)
}
}
const getAverageRating = async (bookId: string) => {
const ratingdocs = await getDocs(
query(collection(db, "rating_book"), where("bookId", "==", bookId))
);
const ratings = ratingdocs.docs.map((doc) => doc.data().rating);
const totalRating = ratings.reduce((acc, curr) => acc+curr, 0);
return ratings.length > 0? totalRating/ratings.length: 0;
}
useEffect(() => {
setLoading(false);
}, );
useEffect(() => {
const fetchRatings = async() => {
const ratings: Record<string, number> = {};
for (const product of products) {
const avgRating = await getAverageRating(product.bk_ID);
ratings[product.bk_ID] = avgRating;
}
setAverageRatings(ratings);
}
fetchRatings();
}, )
const getTrimmedTitle = (title: string) => {
return title.length > 8 ? title.substring(0, 8) + '...' : title;
};
return (
<Grid container spacing={1}>
<Grid item xs={12}>
<Typography variant='h6'>Most Popular</Typography>
<Button
variant="outlined"
onClick={handleFetchAll}
disabled={loading}
style={{ float: 'right', marginTop: '-40px' }}
>
{loading ? 'Loading...' : 'See All'}
</Button>
</Grid>
{products.slice(0, 3).map((product, index) => (
<Grid key={index} item xs={4} onClick={() => onBookClick(product.bk_ID)}>
<Paper elevation={3} sx={{ p: 2, display: 'flex', alignItems: 'center', position: 'relative', height: '100%' }}>
{/* Image */}
<img
src={product.media[0]?.downloadURL}
alt={product.bk_Name}
style={{ width: '50%', height: '70%', objectFit: 'cover', marginRight: '10px' }} // Add marginRight to create space between image and text
/>
{/* Author and Book Title */}
<div style={{ flex: 1 }}>
<Typography variant="subtitle1" gutterBottom sx={{ fontSize: '15px', textAlign: 'right' }}>
{product?.authors[0].at_Name}
</Typography>
<Typography variant="subtitle2" gutterBottom sx={{ fontSize: '15px', textAlign: 'right' }}>
{getTrimmedTitle(product.bk_Name)}
</Typography>
</div>
{/* Rating */}
<div style={{ position: 'absolute', bottom: 0, right: 0 }}>
{/* <Star sx={{ color: '#fdd835', fontSize: 18, marginRight: 1 }} /> */}
<StarRating initialValuePromise={() =>getAverageRating(product.bk_ID)} onClick={() => handleOpenRatingPopup(product)} />
{/* <Typography variant="body2" sx={{ display: 'inline', marginLeft: 1 }}>
{(4.0 + Math.random()).toFixed(1)}
</Typography> */}
</div>
{/* Duration */}
<Typography variant="body2" gutterBottom style={{ position: 'absolute', bottom: 0, left: 0, display: 'flex', alignItems: 'center' }}>
<IconButton style={{ marginRight: '5px', marginLeft: '5px', marginTop: '5px' }} >
<Headset />
</IconButton>
<span style={{ borderBottom: '1px solid #000', top: 10, flexGrow: 1, marginRight: '10px' }} />
{19} min
</Typography>
</Paper>
</Grid>
))}
{openRatingPopup && (
<RatingPopup
product={selectedProduct!}
onSaveRating={handleSaveRating}
onClose={handleCloseRatingPopup}
/>
)}
</Grid>
);
}
export default MostPopular;
and this Star Rating React component
import React, {useEffect, useState} from 'react';
import { Star } from '@mui/icons-material';
interface StarRatingProps {
initialValuePromise: () => Promise<number>;
onClick: () => void;
}
const StarRating = ({initialValuePromise, onClick}: StarRatingProps) => {
const[rating, setRating] = useState<number>(0);
useEffect(() => {
const fetchInitialValue = async() => {
const initialValue = await initialValuePromise();
setRating(initialValue);
}
fetchInitialValue();
}, [initialValuePromise])
const handleClick = () => {
if(onClick) {
onClick();
}
};
return (
<div onClick={handleClick}>
{[...Array(5)].map((_, index) => (
<Star
key={index}
sx={{color: index < Math.floor(rating) ? '#fdd835': 'rgba(0,0,0,.54)', fontSize: 18, marginRight:1}}
/>
))}
<span>{rating.toFixed(1)}</span>
</div>
)
}
export default StarRating;
Currently i am getting error
StarRating.tsx:16 Uncaught (in promise) FirebaseError: Expected first argument to collection() to be a CollectionReference, a DocumentReference or FirebaseFirestore
and
MostPopular.tsx:101 Uncaught (in promise) FirebaseError: Expected first argument to collection() to be a CollectionReference, a DocumentReference or FirebaseFirestore.
Any thoughts on that.
I tried to change the imports but still the error maintains.
Any thoughts or suggestion is highly appreciated.
1