in my nextjs
project everything working fine while npm run dev
but when i try to run npm run build
am getting this error in about 70% of my pages ..here is the error:
Error occurred prerendering page "/saloon-services". Read more: https://nextjs.org/docs/messages/prerender-error
ReferenceError: navigator is not defined
at i.tv [as instanceFactory] (E:Projectssalonhubold_admin.nextserverchunks3070.js:3919:474)
at a.getOrInitializeService (E:Projectssalonhubold_admin.nextserverchunks3070.js:189:2773)
at a.getImmediate (E:Projectssalonhubold_admin.nextserverchunks3070.js:189:724)
at tT (E:Projectssalonhubold_admin.nextserverchunks3070.js:3949:179)
at 17477 (E:Projectssalonhubold_admin.nextserverapp_not-foundpage.js:1:3461)
at t (E:Projectssalonhubold_admin.nextserverwebpack-runtime.js:1:128)
at 43059 (E:Projectssalonhubold_admin.nextserverapp_not-foundpage.js:1:3605)
at Object.t [as require] (E:Projectssalonhubold_admin.nextserverwebpack-runtime.js:1:128)
at require (E:Projectssalonhubold_adminnode_modulesnextdistcompilednext-serverapp-page.runtime.prod.js:16:18490)
at I (E:Projectssalonhubold_adminnode_modulesnextdistcompilednext-serverapp-page.runtime.prod.js:12:94362)
and here is the page that got this erorr /saloon-services
:
'use client'
import { useState, useEffect } from 'react'
import {
getFirestore,
doc,
getDoc,
query,
collection,
where,
getDocs,
setDoc,
addDoc,
deleteDoc
} from 'firebase/firestore'
import { getAuth } from 'firebase/auth'
import { getStorage, ref, uploadBytes, getDownloadURL } from 'firebase/storage'
import Grid from '@mui/material/Grid'
import CircularProgress from '@mui/material/CircularProgress'
import Card from '@mui/material/Card'
import CardContent from '@mui/material/CardContent'
import Typography from '@mui/material/Typography'
import Button from '@mui/material/Button'
import TextField from '@mui/material/TextField'
import Modal from '@mui/material/Modal'
import Box from '@mui/material/Box'
import MenuItem from '@mui/material/MenuItem'
import Select from '@mui/material/Select'
const modalStyle = {
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: 600,
bgcolor: 'background.paper',
border: '2px solid #000',
boxShadow: 24,
p: 4
}
const SaloonServices = () => {
const [isLoading, setIsLoading] = useState(true)
const [saloon, setSaloon] = useState(null)
const [saloonID, setSaloonID] = useState(null)
const [error, setError] = useState(null)
const [services, setServices] = useState([])
const [selectedService, setSelectedService] = useState(null)
const [modalOpen, setModalOpen] = useState(false)
const [categories, setCategories] = useState([]) // New state for categories
const [selectedCategoryId, setSelectedCategoryId] = useState('') // State to store selected category ID
const [newService, setNewService] = useState({
details_ar: '',
details_en: '',
highlights_ar: '',
highlights_en: '',
image: '',
category: {
name_en: '',
name_ar: '',
icon: ''
},
for_male: false,
for_female: false,
starting_price: 0,
min_time_mins: 0,
max_time_mins: 0
})
const [imageFile, setImageFile] = useState(null)
const fetchSalonDetails = async () => {
try {
const db = getFirestore()
const auth = getAuth()
const currentUser = auth.currentUser
if (!currentUser) {
throw new Error('No authenticated user found')
}
const userDocRef = doc(db, 'User', currentUser.uid)
const userDoc = await getDoc(userDocRef)
if (!userDoc.exists()) {
throw new Error('User not found')
}
const salonsQuery = query(collection(db, 'salons'), where('owner', '==', userDocRef))
const querySnapshot = await getDocs(salonsQuery)
if (querySnapshot.empty) {
throw new Error('Salon not found')
}
const salonDoc = querySnapshot.docs[0]
const salonData = salonDoc.data()
setSaloon(salonData)
setSaloonID(salonDoc.id)
fetchServices(salonDoc.id)
} catch (err) {
console.error('Error fetching salon details:', err)
setError(err.message)
setIsLoading(false)
}
}
const fetchServices = async salonId => {
try {
const db = getFirestore()
console.log('fetching saloon service for the saloon with id : ', salonId)
const salonServicesQuery = query(
collection(db, 'salon_services'),
where('salon', '==', doc(db, 'salons', salonId))
)
const querySnapshot = await getDocs(salonServicesQuery)
if (querySnapshot.empty) {
setServices([])
} else {
const servicesData = querySnapshot.docs.map(doc => {
const data = doc.data()
return {
id: doc.id, // Ensure the ID is correctly assigned
...data // Spread the rest of the data
}
})
console.log(servicesData) // Check the output in the console
setServices(servicesData) // Update the state with the services data
}
} catch (error) {
console.error('Failed to fetch services:', error)
setError(error.message)
} finally {
setIsLoading(false)
}
}
const fetchCategories = async () => {
try {
const db = getFirestore()
const categoriesQuery = query(collection(db, 'services'))
const querySnapshot = await getDocs(categoriesQuery)
const categoriesData = querySnapshot.docs.map(doc => ({
id: doc.id,
name_en: doc.data().name_en,
name_ar: doc.data().name_ar
}))
setCategories(categoriesData)
} catch (error) {
console.error('Failed to fetch categories:', error)
}
}
useEffect(() => {
fetchSalonDetails()
fetchCategories() // Fetch categories when the component mounts
}, [])
const handleOpenModal = (service = null) => {
setSelectedService(service)
setNewService({
id: service ? service.id : '', // Access and set the service ID
details_ar: service ? service.details_ar : '',
details_en: service ? service.details_en : '',
highlights_en: service ? service.highlights_en : '',
highlights_ar: service ? service.highlights_ar : '',
starting_price: service ? service.starting_price : 0,
min_time_mins: service ? service.min_time_mins : 0,
max_time_mins: service ? service.max_time_mins : 0
})
setImageFile(null) // Reset image file
// setSelectedCategoryId(service ? service.category.id : '') // Set the selected category ID
setModalOpen(true)
}
const handleCloseModal = () => {
setSelectedService(null)
setNewService({
name: '',
description: '',
image: '',
category: { name_en: '', name_ar: '', icon: '' },
for_male: false,
for_female: false,
starting_price: 0,
min_time_mins: 0,
max_time_mins: 0
})
setImageFile(null) // Reset image file
setSelectedCategoryId('') // Reset selected category ID
setModalOpen(false)
}
const handleImageChange = e => {
const file = e.target.files[0]
if (file) {
setImageFile(file)
}
}
const handleSaveService = async () => {
try {
const db = getFirestore()
console.log('before service Data')
const serviceData = {
details_en: newService.details_en,
details_ar: newService.details_ar,
highlights_ar: newService.highlights_ar,
highlights_en: newService.highlights_en,
starting_price: newService.starting_price,
min_time_mins: newService.min_time_mins,
max_time_mins: newService.max_time_mins,
service: doc(db, 'services', selectedCategoryId),
salon: doc(db, 'salons', saloonID)
}
console.log('after service Data', serviceData)
console.log('saloon id is :', saloonID)
console.log(serviceData)
if (selectedService) {
// Update existing service
await setDoc(doc(db, 'salon_services', selectedService.id), serviceData)
} else {
// Add new service with auto-generated ID
await addDoc(collection(db, 'salon_services'), serviceData)
}
fetchServices(saloonID)
handleCloseModal()
} catch (error) {
console.error('Failed to save service:', error)
}
}
const handleDeleteService = async id => {
try {
const db = getFirestore()
await deleteDoc(doc(db, 'salon_services', id)) // Mark service as deleted or remove it
fetchServices(saloonID)
} catch (error) {
console.error('Failed to delete service:', error)
}
}
if (isLoading) {
return (
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100vh' }}>
<CircularProgress />
</div>
)
}
if (error) {
return (
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100vh' }}>
<Typography color='error'>{error}</Typography>
</div>
)
}
if (!saloon) {
return (
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100vh' }}>
<Typography>No salon data available</Typography>
</div>
)
}
if (typeof window !== 'undefined') {
return (
<Grid container style={{ height: '100vh', padding: 20 }} direction='column' alignItems='center'>
<Card style={{ width: '100%', maxWidth: 800, padding: 20 }}>
<CardContent>
<Typography variant='h5' gutterBottom>
Salon Services
</Typography>
<Button variant='contained' color='primary' onClick={() => handleOpenModal()}>
Add New Service
</Button>
<Grid container spacing={2} style={{ marginTop: 20 }}>
{services.map(service => (
<Grid item xs={12} sm={6} md={4} key={service.id}>
<Card>
<CardContent>
<Typography variant='h6'>{service.service.name_en}</Typography>
<Typography variant='body2'>{service.details_en}</Typography>
<Typography variant='body2'>{service.starting_price} </Typography>
<Button
variant='outlined'
color='primary'
onClick={() => handleOpenModal(service)}
style={{ marginRight: 10 }}
>
Edit
</Button>
<Button variant='outlined' color='secondary' onClick={() => handleDeleteService(service.id)}>
Delete
</Button>
</CardContent>
</Card>
</Grid>
))}
</Grid>
</CardContent>
</Card>
<Modal open={modalOpen} onClose={handleCloseModal}>
<Box sx={modalStyle}>
<Typography variant='h6' component='h2'>
Service Details
</Typography>
{/* <TextField
fullWidth
label="Service Name"
value={newService.name}
onChange={(e) => setNewService({ ...newService, name: e.target.value })}
margin="normal"
/>*/}
<TextField
fullWidth
label='Service Details English'
value={newService.details_en}
onChange={e => setNewService({ ...newService, details_en: e.target.value })}
margin='normal'
/>
<TextField
fullWidth
label='Service Details Arabic'
value={newService.details_ar}
onChange={e => setNewService({ ...newService, details_ar: e.target.value })}
margin='normal'
/>
<TextField
fullWidth
label='Service Highlights English'
value={newService.highlights_en}
onChange={e => setNewService({ ...newService, highlights_en: e.target.value })}
margin='normal'
/>
<TextField
fullWidth
label='Service Highlights Arabic'
value={newService.highlights_ar}
onChange={e => setNewService({ ...newService, highlights_ar: e.target.value })}
margin='normal'
/>
<TextField
fullWidth
label='Starting Price'
type='number'
value={newService.starting_price}
onChange={e => setNewService({ ...newService, starting_price: parseFloat(e.target.value) })}
margin='normal'
/>
<TextField
fullWidth
label='Minimum Time (mins)'
type='number'
value={newService.min_time_mins}
onChange={e => setNewService({ ...newService, min_time_mins: parseFloat(e.target.value) })}
margin='normal'
/>
<TextField
fullWidth
label='Maximum Time (mins)'
type='number'
value={newService.max_time_mins}
onChange={e => setNewService({ ...newService, max_time_mins: parseFloat(e.target.value) })}
margin='normal'
/>
<Select
fullWidth
value={selectedCategoryId}
onChange={e => {
const selectedCategory = categories.find(category => category.id === e.target.value)
setSelectedCategoryId(e.target.value)
setNewService({
...newService,
category: {
...newService.category,
name_en: selectedCategory.name_en,
name_ar: selectedCategory.name_ar
}
})
}}
margin='normal'
>
<MenuItem value=''>
<em>Select Category</em>
</MenuItem>
{categories.map(category => (
<MenuItem key={category.id} value={category.id}>
{category.name_en} ({category.name_ar})
</MenuItem>
))}
</Select>
<Box mt={2}>
<Button variant='contained' color='primary' onClick={handleSaveService}>
Save Service
</Button>
<Button variant='outlined' color='secondary' onClick={handleCloseModal} style={{ marginLeft: 10 }}>
Cancel
</Button>
</Box>
</Box>
</Modal>
</Grid>
)
} else {
;<div>Loading ..</div>
}
}
export default SaloonServices