diff --git a/src/AppRouter.jsx b/src/AppRouter.jsx index e751fd5..3606d2f 100644 --- a/src/AppRouter.jsx +++ b/src/AppRouter.jsx @@ -10,7 +10,7 @@ const Dashboard = lazy(() => import('./pages/dashboard/Dashboard')); const ErrorPage = lazy(() => import('./pages/error/Error')); const Login = lazy(() => import('./component/login/Login')); const Logout = lazy(() => import('./pages/logout/Logout')); -const Profile = lazy(() => import('./pages/profile/Profile')); +const Profile = lazy(() => import('./component/profile/Profile')); const Administration = lazy(() => import('./pages/administration/Administration')); const Home = lazy(() => import('./pages/home/Home')); const SignUp = lazy(() => import('./pages/signup/SignUp')); diff --git a/src/component/deleteAccount/DeleteAccount.jsx b/src/component/deleteAccount/DeleteAccount.jsx index b56b20a..befe2cd 100644 --- a/src/component/deleteAccount/DeleteAccount.jsx +++ b/src/component/deleteAccount/DeleteAccount.jsx @@ -1,6 +1,6 @@ // src/component/DeleteAccount.jsx import { useState, useContext } from 'react'; -import { auth } from '../../firebaseConfig'; +import { auth } from '../../utils/firebaseConfig'; import { deleteUser, reauthenticateWithCredential, EmailAuthProvider } from 'firebase/auth'; import { useNavigate } from 'react-router-dom'; import { AuthContext } from '../../context/AuthContext'; // Import AuthContext for å få tilgang til logout-funksjonen diff --git a/src/component/login/Login.jsx b/src/component/login/Login.jsx index 4288576..8938c8d 100644 --- a/src/component/login/Login.jsx +++ b/src/component/login/Login.jsx @@ -1,7 +1,7 @@ import { useState, useContext, useEffect } from 'react'; import { AuthContext } from '../../context/AuthContext'; import { useNavigate } from 'react-router-dom'; -import { auth } from '../../firebaseConfig'; +import { auth } from '../../utils/firebaseConfig'; import { signInWithEmailAndPassword } from 'firebase/auth'; import '../../component/login/login.css'; diff --git a/src/component/profile/Profile.jsx b/src/component/profile/Profile.jsx new file mode 100644 index 0000000..cdceb4b --- /dev/null +++ b/src/component/profile/Profile.jsx @@ -0,0 +1,28 @@ +// src/component/profile/Profile.jsx +import useProfile from '../../hooks/useProfile'; + +const Profile = () => { + const { profileData, loading } = useProfile(); + + if (loading) { + return
Loading...
; + } + + if (!profileData) { + return
No profile data found.
; + } + + return ( +
+

Your Profile

+

Name: {profileData.name}

+

Email: {profileData.email}

+

City: {profileData.city}

+

Country: {profileData.country}

+

hh: {profileData.address}

+ {/* Legg til flere felt etter behov */} +
+ ); +}; + +export default Profile; diff --git a/src/component/profile/ProfileForm.jsx b/src/component/profile/ProfileForm.jsx new file mode 100644 index 0000000..02e6362 --- /dev/null +++ b/src/component/profile/ProfileForm.jsx @@ -0,0 +1,39 @@ +import PropTypes from 'prop-types'; + +const ProfileForm = ({ profileData, setProfileData, isEditing, setIsEditing, uploadProfileImage }) => { + return ( +
+
+ + setProfileData({ ...profileData, name: e.target.value })} + disabled={!isEditing} + /> +
+ {/* ... andre input-felter ... */} +
+ + uploadProfileImage(e.target.files[0])} // Kall uploadProfileImage her + disabled={!isEditing} + /> +
+ +
+ ); +}; + +ProfileForm.propTypes = { + profileData: PropTypes.object.isRequired, + setProfileData: PropTypes.func.isRequired, + isEditing: PropTypes.bool.isRequired, + setIsEditing: PropTypes.func.isRequired, + uploadProfileImage: PropTypes.func.isRequired, // Sørg for at denne er inkludert +}; + +export default ProfileForm; + diff --git a/src/component/profile/ProfileImageUpload.jsx b/src/component/profile/ProfileImageUpload.jsx new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/component/profile/ProfileImageUpload.jsx @@ -0,0 +1 @@ + diff --git a/src/component/profile/profile.css b/src/component/profile/profile.css new file mode 100644 index 0000000..2595fc3 --- /dev/null +++ b/src/component/profile/profile.css @@ -0,0 +1,93 @@ +/* ProfileForm.css */ + +.profile-form-container { + background-color: #f8f9fa; + border-radius: 8px; + padding: 20px; + max-width: 600px; + margin: auto; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); + + +.profile-header { + text-align: center; + margin-bottom: 20px; +} + +.profile-header h2 { + margin: 10px 0 5px; + font-size: 24px; +} + +.profile-header h3 { + margin: 0; + font-size: 16px; + color: #666; +} + +.profile-image { + width: 100px; + height: 100px; + border-radius: 50%; + object-fit: cover; + margin-bottom: 15px; +} + +.profile-form { + display: flex; + flex-direction: column; +} + +.input-group { + margin-bottom: 15px; +} + +.input-group label { + margin-bottom: 5px; + font-weight: bold; + color: #333; +} + +.input-group input { + padding: 10px; + border: 1px solid #ccc; + border-radius: 5px; + font-size: 16px; +} + +.input-group input:focus { + border-color: #007bff; + outline: none; +} + +.action-button, .save-button { + padding: 10px; + border: none; + border-radius: 5px; + background-color: #007bff; + color: white; + font-size: 16px; + cursor: pointer; + margin-top: 10px; +} + +.action-button:hover, .save-button:hover { + background-color: #0056b3; +} + +/* Responsiv design */ +@media (max-width: 600px) { + .profile-form-container { + padding: 10px; + } + + .profile-image { + width: 80px; + height: 80px; + } + + .action-button, .save-button { + width: 100%; + } +} +} \ No newline at end of file diff --git a/src/context/AuthContext.jsx b/src/context/AuthContext.jsx index 6d7852d..a63f6e9 100644 --- a/src/context/AuthContext.jsx +++ b/src/context/AuthContext.jsx @@ -31,7 +31,7 @@ // src/context/AuthContext.jsx import { createContext, useState, useContext, useEffect } from 'react'; import PropTypes from 'prop-types'; // Importer PropTypes -import { auth } from '../firebaseConfig'; // Importer Firebase-authentisering +import { auth } from '../utils/firebaseConfig'; // Importer Firebase-authentisering import { onAuthStateChanged, setPersistence, browserLocalPersistence } from 'firebase/auth'; export const AuthContext = createContext(); diff --git a/src/firebaseConfig.js b/src/firebaseConfig.js deleted file mode 100644 index 21c16ac..0000000 --- a/src/firebaseConfig.js +++ /dev/null @@ -1,67 +0,0 @@ -// src/firebaseConfig.js -import { initializeApp } from 'firebase/app'; -import { getFirestore } from 'firebase/firestore'; -import { getAuth } from 'firebase/auth'; -import { getAnalytics } from 'firebase/analytics'; -import { getStorage } from 'firebase/storage'; // Importer getStorage fra Firebase - -// Firebase-konfigurasjon -const firebaseConfig = { - apiKey: "AIzaSyBf-UV2wzdvuGdUdAb4dUVKQln6hYJWtJY", - authDomain: "bookingpage-bef4a.firebaseapp.com", - projectId: "bookingpage-bef4a", - storageBucket: "bookingpage-bef4a.appspot.com", - messagingSenderId: "676485037932", - appId: "1:676485037932:web:91736d92b774a628a23d49", - measurementId: "G-6JTVF4PKK8" -}; - -// Funksjon for å logge Firebase-feil -const logError = (error) => { - console.error("Firebase Error:", error.message); - // Her kan du utvide for å logge til et eksternt verktøy, som Sentry eller LogRocket -}; - -// Initialiser Firebase-appen med feilhåndtering -let app; -try { - app = initializeApp(firebaseConfig); -} catch (error) { - logError(error); -} - -// Initialiser Firestore og fang opp eventuelle feil -let firestore; -try { - firestore = getFirestore(app); -} catch (error) { - logError(error); -} - -// Initialiser Firebase Authentication og fang opp eventuelle feil -let auth; -try { - auth = getAuth(app); -} catch (error) { - logError(error); -} - -// Initialiser Firebase Storage og fang opp eventuelle feil -let storage; -try { - storage = getStorage(app); -} catch (error) { - logError(error); -} - -// Initialiser Analytics og fang opp eventuelle feil -let analytics; -try { - analytics = getAnalytics(app); -} catch (error) { - logError(error); -} - -// Eksporter alle nødvendige objekter -export { firestore, auth, storage, analytics }; - diff --git a/src/hooks/useAuth.js b/src/hooks/useAuth.js new file mode 100644 index 0000000..f8d9b66 --- /dev/null +++ b/src/hooks/useAuth.js @@ -0,0 +1,41 @@ +// src/hooks/useAuth.js +import { useState, useEffect } from 'react'; +import { auth } from '../utils/firebaseConfig'; +import { updateEmail, updatePassword, onAuthStateChanged } from 'firebase/auth'; + +const useAuth = () => { + const [currentUser, setCurrentUser] = useState(null); + + useEffect(() => { + const unsubscribe = onAuthStateChanged(auth, (user) => { + console.log("User state changed:", user); // Logg brukerstatusendringer + setCurrentUser(user); // Oppdaterer currentUser når autentiseringstilstanden endres + }); + + return () => unsubscribe(); // Rydder opp abonnementet når komponenten avmonteres + }, []); + + const updateUserEmail = async (newEmail) => { + if (newEmail !== auth.currentUser.email) { + await updateEmail(auth.currentUser, newEmail); + console.log("User email updated to:", newEmail); // Logg når e-post oppdateres + } + }; + + const updateUserPassword = async (newPassword) => { + if (newPassword) { + await updatePassword(auth.currentUser, newPassword); + console.log("User password updated."); // Logg når passord oppdateres + } + }; + + return { + currentUser, // Returner currentUser for bruk i komponentene + updateUserEmail, + updateUserPassword, + }; +}; + +export default useAuth; + + diff --git a/src/hooks/useProfile.js b/src/hooks/useProfile.js new file mode 100644 index 0000000..cdf2306 --- /dev/null +++ b/src/hooks/useProfile.js @@ -0,0 +1,46 @@ +// src/hooks/useProfile.js +import { useState, useEffect } from 'react'; +import { firestore } from '../utils/firebaseConfig'; +import { doc, getDoc } from 'firebase/firestore'; +import useAuth from './useAuth'; + +const useProfile = () => { + const [profileData, setProfileData] = useState(null); + const [loading, setLoading] = useState(true); + const { currentUser } = useAuth(); + + useEffect(() => { + const fetchProfileData = async () => { + if (!currentUser) { + console.log("No current user found, setting loading to false."); // Logging if no user is found + setLoading(false); + return; + } + + console.log("Current User UID:", currentUser.uid); // Logg UID-en her + + try { + const userDocRef = doc(firestore, 'users', currentUser.uid); + const userDoc = await getDoc(userDocRef); + + if (userDoc.exists()) { + setProfileData(userDoc.data()); + console.log("Profile data loaded:", userDoc.data()); // Logg profildata ved henting + } else { + console.error("User document does not exist."); + } + } catch (error) { + console.error("Error fetching user profile:", error); + } finally { + setLoading(false); + } + }; + + fetchProfileData(); + }, [currentUser]); + + return { profileData, loading }; +}; + +export default useProfile; + diff --git a/src/pages/administration/Administration.jsx b/src/pages/administration/Administration.jsx index 2fd1c84..b0f9df7 100644 --- a/src/pages/administration/Administration.jsx +++ b/src/pages/administration/Administration.jsx @@ -1,6 +1,6 @@ // src/pages/Administration/Administration.jsx import { useEffect, useState } from 'react'; -import { firestore } from '../../firebaseConfig'; +import { firestore } from '../../utils/firebaseConfig'; import { collection, getDocs, deleteDoc, doc } from 'firebase/firestore'; import './Administration.css'; diff --git a/src/pages/nav/Navigation.jsx b/src/pages/nav/Navigation.jsx index ef57cfa..ba1c808 100644 --- a/src/pages/nav/Navigation.jsx +++ b/src/pages/nav/Navigation.jsx @@ -44,6 +44,7 @@ const Navigation = () => {
  • Dashboard
  • Sign up
  • Delete Account
  • +
  • profile
  • diff --git a/src/pages/profile/Profile.jsx b/src/pages/profile/Profile.jsx deleted file mode 100644 index 9f40de0..0000000 --- a/src/pages/profile/Profile.jsx +++ /dev/null @@ -1,9 +0,0 @@ - - -const Profile = () => { - return ( -
    Profile
    - ) -} - -export default Profile \ No newline at end of file diff --git a/src/pages/profile/profile.css b/src/pages/profile/profile.css deleted file mode 100644 index e69de29..0000000 diff --git a/src/pages/signup/SignUp.jsx b/src/pages/signup/SignUp.jsx index b4c515e..5bd9943 100644 --- a/src/pages/signup/SignUp.jsx +++ b/src/pages/signup/SignUp.jsx @@ -1,16 +1,13 @@ // src/component/signup/SignUp.jsx import { useState } from 'react'; -import { auth, firestore } from '../../firebaseConfig'; -import { createUserWithEmailAndPassword, signInWithEmailAndPassword } from 'firebase/auth'; -import { collection, addDoc } from 'firebase/firestore'; +import { auth, firestore, storage } from '../../utils/firebaseConfig'; // Importer nødvendige moduler +import { createUserWithEmailAndPassword } from 'firebase/auth'; +import { doc, setDoc } from 'firebase/firestore'; // Importer setDoc for å oppdatere dokumenter import { useNavigate } from 'react-router-dom'; import { signUpSchema } from '../../assets/validationSchema'; import CityAutocomplete from '../../component/AutoComplete/CityAutoComplete'; import CountryAutocomplete from '../../component/AutoComplete/CountryAutoComplete'; -import { storage } from '../../firebaseConfig'; -import { ref, uploadBytes, getDownloadURL } from 'firebase/storage'; - - +import { ref, uploadBytes, getDownloadURL } from 'firebase/storage'; // Importer for opplasting av bilder import './signup.css'; const SignUp = () => { @@ -66,17 +63,20 @@ const SignUp = () => { try { await signUpSchema.validate(formData, { abortEarly: false }); + // Opprett bruker med Firebase Authentication const userCredential = await createUserWithEmailAndPassword(auth, formData.email, formData.password); const user = userCredential.user; let profileImageUrl = ''; + // Last opp profilbilde hvis det er valgt if (profileImage) { const imageRef = ref(storage, `profileImages/${user.uid}`); await uploadBytes(imageRef, profileImage); - profileImageUrl = await getDownloadURL(imageRef); + profileImageUrl = await getDownloadURL(imageRef); // Hent URL for det opplastede bildet } - await addDoc(collection(firestore, 'users'), { + // Opprett eller oppdater dokumentet for brukeren i Firestore + await setDoc(doc(firestore, 'users', user.uid), { uid: user.uid, name: formData.name, email: formData.email, @@ -86,14 +86,13 @@ const SignUp = () => { postalPlace: formData.postalPlace, city: formData.city, country: formData.country, - profileImage: profileImageUrl, + profileImage: profileImageUrl, // Lagre URL for profilbilde }); setSuccess('User registered successfully!'); - await signInWithEmailAndPassword(auth, formData.email, formData.password); - - navigate('/dashboard'); + // Naviger til profilen etter registrering + navigate('/profile'); } catch (err) { if (err.name === 'ValidationError') { @@ -243,4 +242,10 @@ const SignUp = () => { ); }; -export default SignUp; \ No newline at end of file +export default SignUp; + + + + + + diff --git a/src/utils/fireBaseStorage.js b/src/utils/fireBaseStorage.js new file mode 100644 index 0000000..36bc19f --- /dev/null +++ b/src/utils/fireBaseStorage.js @@ -0,0 +1,12 @@ +import { getStorage } from 'firebase/storage'; +import { logError } from './firebaseConfig'; + +let storage; + +try { + storage = getStorage(); +} catch (error) { + logError('Storage', error); +} + +export { storage }; diff --git a/src/utils/firebaseAuth.js b/src/utils/firebaseAuth.js new file mode 100644 index 0000000..eb654af --- /dev/null +++ b/src/utils/firebaseAuth.js @@ -0,0 +1,12 @@ +import { getAuth } from 'firebase/auth'; +import { logError } from './firebaseConfig'; + +let auth; + +try { + auth = getAuth(); +} catch (error) { + logError('Authentication', error); +} + +export { auth }; diff --git a/src/utils/firebaseConfig.js b/src/utils/firebaseConfig.js new file mode 100644 index 0000000..fb87325 --- /dev/null +++ b/src/utils/firebaseConfig.js @@ -0,0 +1,43 @@ +// firebaseConfig.js +import { initializeApp } from 'firebase/app'; +import { getFirestore } from 'firebase/firestore'; +import { getAuth } from 'firebase/auth'; +import { getAnalytics } from 'firebase/analytics'; +import { getStorage } from 'firebase/storage'; // Importer getStorage fra Firebase + +// Firebase-konfigurasjon +const firebaseConfig = { + apiKey: "AIzaSyBf-UV2wzdvuGdUdAb4dUVKQln6hYJWtJY", + authDomain: "bookingpage-bef4a.firebaseapp.com", + projectId: "bookingpage-bef4a", + storageBucket: "bookingpage-bef4a.appspot.com", + messagingSenderId: "676485037932", + appId: "1:676485037932:web:91736d92b774a628a23d49", + measurementId: "G-6JTVF4PKK8" +}; + +// Initialiser Firebase-appen +let app; +try { + app = initializeApp(firebaseConfig); +} catch (error) { + console.error("Error initializing Firebase app:", error); +} + +// Initialiser Firestore +const firestore = getFirestore(app); + +// Initialiser Auth +const auth = getAuth(app); + +// Initialiser Storage +const storage = getStorage(app); + +// Initialiser Analytics +const analytics = getAnalytics(app); + +// Eksporter nødvendige moduler +export { firestore, auth, storage, analytics }; + + + diff --git a/src/utils/firebaseFireStorm.js b/src/utils/firebaseFireStorm.js new file mode 100644 index 0000000..f6b3492 --- /dev/null +++ b/src/utils/firebaseFireStorm.js @@ -0,0 +1,12 @@ +import { getFirestore } from 'firebase/firestore'; +import { logError } from './firebaseConfig'; + +let firestore; + +try { + firestore = getFirestore(); +} catch (error) { + logError('Firestore', error); +} + +export { firestore }; diff --git a/src/utils/firebaseUtils.js b/src/utils/firebaseUtils.js new file mode 100644 index 0000000..496e758 --- /dev/null +++ b/src/utils/firebaseUtils.js @@ -0,0 +1,64 @@ +import { firestore, storage } from './firebaseConfig'; // Pass på at banen er korrekt +import { doc, getDoc, setDoc } from 'firebase/firestore'; +import { ref, uploadBytes, getDownloadURL } from 'firebase/storage'; + +// Hent brukerdata fra Firestore +export const getUserData = async (userId) => { + try { + const userDoc = await getDoc(doc(firestore, 'users', userId)); + if (userDoc.exists()) { + return userDoc.data(); + } else { + throw new Error("User not found"); + } + } catch (error) { + console.error("Error fetching user data:", error); + throw error; + } +}; + +// Oppdater brukerdata i Firestore +export const updateUserData = async (userId, data) => { + // Validerer data før oppdatering + if (!data || typeof data !== 'object') { + throw new Error("Invalid data provided"); + } + + try { + await setDoc(doc(firestore, 'users', userId), data, { merge: true }); + return "User data updated successfully"; // Tilbakemelding + } catch (error) { + console.error("Error updating user data:", error); + throw error; + } +}; + +// Last opp profilbilde til Firebase Storage og hent URL +export const uploadProfileImage = async (userId, file) => { + // Validerer filen før opplasting + if (!file || !(file instanceof Blob)) { + throw new Error("Invalid file provided"); + } + + try { + const imageRef = ref(storage, `profileImages/${userId}`); + await uploadBytes(imageRef, file); + const imageUrl = await getDownloadURL(imageRef); + return imageUrl; // Returner kun URL-en som en string + } catch (error) { + console.error("Error uploading profile image:", error); + throw error; + } +}; + +// Hent profilbilde-URL for bruker +export const getProfileImageUrl = async (userId) => { + try { + const imageRef = ref(storage, `profileImages/${userId}`); + const imageUrl = await getDownloadURL(imageRef); + return imageUrl; + } catch (error) { + console.error("Error fetching profile image URL:", error); + throw error; + } +}; \ No newline at end of file diff --git a/vite.config copy.js b/vite.config copy.js new file mode 100644 index 0000000..ecb5e8d --- /dev/null +++ b/vite.config copy.js @@ -0,0 +1,65 @@ +// vite.config.js +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react-swc'; +import { visualizer } from 'rollup-plugin-visualizer'; +import { VitePWA } from 'vite-plugin-pwa'; + +// Setter base-URL basert på 'mode'-argumentet +export default defineConfig(({ mode }) => { + const base = mode === 'production' ? '/bookingapp/' : '/'; + + return { + plugins: [ + react(), + visualizer({ open: true }), // Åpner en grafisk rapport automatisk etter bygging + VitePWA({ + registerType: 'autoUpdate', + includeAssets: ['favicon.ico', 'robots.txt', 'apple-touch-icon.png'], + manifest: { + name: 'Booking App', + short_name: 'Booking', + theme_color: '#ffffff', + icons: [ + { + src: 'icon-192x192.png', + sizes: '192x192', + type: 'image/png', + }, + { + src: 'icon-512x512.png', + sizes: '512x512', + type: 'image/png', + }, + ], + }, + }), + ], + base, // Dynamisk base-URL + build: { + outDir: 'dist', + rollupOptions: { + output: { + manualChunks(id) { + if (id.includes('node_modules')) { + return id.toString().split('node_modules/')[1].split('/')[0].toString(); + } + }, + }, + }, + }, + server: { + hmr: { + overlay: false, + }, + historyApiFallback: true, + }, + optimizeDeps: { + include: [ + 'firebase/app', + 'firebase/auth', + 'firebase/firestore', + 'firebase/analytics' + ] + } + }; +});