diff --git a/app/account/page.tsx b/app/account/page.tsx index ce28921..4802836 100644 --- a/app/account/page.tsx +++ b/app/account/page.tsx @@ -1,14 +1,17 @@ "use client"; +import updateSetupDefaultProfile from "@/utils/firebase/db/updateSetupDefaultProfile"; import NotificationPopup from "@/components/general/notificationPopup"; import updateDisplayName from "@/utils/firebase/db/updateDisplayName"; import updateProfileType from "@/utils/firebase/db/updateProfileType"; import deleteAccount from "@/utils/firebase/account/deleteAccount"; import googleSignOut from "@/utils/firebase/account/googleSignOut"; +import updateProfiles from "@/utils/firebase/db/updateProfiles"; import ConfirmPopup from "@/components/misc/confirmPopup"; import getUserDoc from "@/utils/firebase/db/getUserDoc"; import { useAuthContext } from "@context/authContext"; import Dropdown from "@/components/general/dropdown"; +import Slider from "@/components/general/slider"; import Button from "@/components/general/button"; import Input from "@/components/general/input"; import { useState, useEffect } from "react"; @@ -18,7 +21,6 @@ import Image from "next/image"; export default function History() { // general - const [userDoc, setUserDoc] = useState({} as any); const { user } = useAuthContext() as { user: any }; const router = useRouter(); @@ -38,6 +40,21 @@ export default function History() { const [profileType, setProfileType] = useState("Private"); const [photoURL, setPhotoURL] = useState(""); + // profiles + const [profiles, setProfiles] = useState([] as any[]); + const [currentProfile, setCurrentProfile] = useState({} as any); + const [profileName, setProfileName] = useState(""); + const [setupDefaultProfile, setSetupDefaultProfile] = useState(0); + + // scoring + const [location, setLocation] = useState(""); + const [distanceUnit, setDistanceUnit] = useState(""); + const [distance, setDistance] = useState(0); + const [ends, setEnds] = useState(0); + const [arrowsPerEnd, setArrowsPerEnd] = useState(0); + const [splitEnds, setSplitEnds] = useState(0); + const [bow, setBow] = useState(""); + useEffect(() => { if (!user) return; auth.onAuthStateChanged((user) => { @@ -49,14 +66,32 @@ export default function History() { }); getUserDoc(auth.currentUser).then((doc: any) => { - setUserDoc(doc); setNamePlaceholder(doc.displayName); setProfileType(doc.profileType); + setSetupDefaultProfile(doc.setupDefaultProfile); + processProfiles(doc.profiles); const bigPhotoURL = doc.photoURL.replace("s96-c", "s500-c"); setPhotoURL(bigPhotoURL); }); }, [user]); + const processProfiles = async (dbProfiles: string[]) => { + loadProfile(dbProfiles[0]); + setProfiles(dbProfiles); + }; + + const loadProfile = (profile: any) => { + setCurrentProfile(profile); + setLocation(profile.location); + setProfileName(profile.profileName); + setDistanceUnit(profile.distanceUnit); + setDistance(profile.distance); + setEnds(profile.ends); + setArrowsPerEnd(profile.arrowsPerEnd); + setSplitEnds(profile.splitEnds); + setBow(profile.bow); + }; + const updateName = () => { updateDisplayName(auth.currentUser, name); setName(""); @@ -80,40 +115,192 @@ export default function History() { setDeletePopup(false); }; + const saveProfile = () => { + const profileId = currentProfile.profileId; + const tempProfiles = [...profiles]; // Create a shallow copy of the profiles array + + if (!checkIfUniqueProfileName(profileName, profileId)) { + setNotification(true); + setNotificationType("error"); + setNotificationTitle("Error"); + setNotificationMessage("Profile name already exists"); + return; + } + + let tempProfile = { ...currentProfile }; // Create a shallow copy of the currentProfile object + tempProfile.location = location; + tempProfile.profileName = profileName; + tempProfile.distanceUnit = distanceUnit; + tempProfile.distance = distance; + tempProfile.ends = ends; + tempProfile.arrowsPerEnd = arrowsPerEnd; + tempProfile.splitEnds = splitEnds; + tempProfile.bow = bow; + + if (JSON.stringify(currentProfile) === JSON.stringify(tempProfile)) { + setNotification(true); + setNotificationType("error"); + setNotificationTitle("Error"); + setNotificationMessage("No changes made"); + return; + } + + tempProfiles[profileId - 1] = tempProfile; + setProfiles(tempProfiles); + updateProfiles(user, tempProfiles); + + setNotification(true); + setNotificationType("success"); + setNotificationTitle("Success"); + setNotificationMessage("Profile updated"); + }; + + const checkIfUniqueProfileName = (name: string, profileId: number) => { + for (let i = 0; i < profiles.length; i++) { + if ( + profiles[i].profileName == name && + profiles[i].profileId != profileId + ) { + return false; + } + } + return true; + }; + + const switchProfile = (newProfileName: any) => { + profiles.forEach((profile) => { + if (profile.profileName == newProfileName) { + loadProfile(profile); + return; + } + }); + }; + + const updateSetupProfile = () => { + setSetupDefaultProfile(currentProfile.profileId); + updateSetupDefaultProfile(user, currentProfile.profileId); + }; + return ( <> -
- use pfp -
-
- +
+

Account Settings

+
+ use pfp -
+
+
+
+
+
+
+

Scoring Profiles

+
+
+ + +
+
+ + + + +
+
+ {profiles ? ( + profile.profileName)} + setSelected={setProfileName} + onNewSelection={switchProfile} + /> + ) : null} + +
-
-
- -
+ + {deletePopup ? ( +

404

+

Page not found

+ + ); +} diff --git a/app/page.tsx b/app/page.tsx index 45f9c27..597657d 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,218 +1,9 @@ -"use client"; - -import NotificationPopup from "@/components/general/notificationPopup"; -import FinalScoringStats from "@/components/scoring/finalScoringStats"; -import SessionOptions from "@/components/scoring/sessionOptions"; -import ScoreSetupMenu from "@/components/scoring/scoreSetupMenu"; -import SaveScorePopup from "@/components/scoring/saveScorePopup"; -import ScoringChart from "@/components/scoring/scoringChart"; -import SignUpPopup from "@/components/misc/signUpPopup"; -import { useAuthContext } from "@context/authContext"; -import countTotals from "@/utils/score/countTotals"; -import saveSession from "@/utils/score/saveSession"; -import { useState, useEffect } from "react"; - export default function Home() { - const [setup, setSetup] = useState(true); - const [finished, setFinished] = useState(false); - const [savingPopup, setSavingPopup] = useState(false); - const [signedIn, setSignedIn] = useState(false); - const [signUpPopup, setSignUpPopup] = useState(false); - - const { user } = useAuthContext() as { user: any }; - - const [location, setLocation] = useState(""); - const [distanceUnit, setDistanceUnit] = useState(""); - const [distance, setDistance] = useState(18); - const [ends, setEnds] = useState(10); - const [arrowsPerEnd, setArrowsPerEnd] = useState(3); - const [splitEnds, setSplitEnds] = useState(1); - const [bow, setBow] = useState(""); - - const [data, setData] = useState({} as any); - - const [notification, setNotification] = useState(false); - const [notificationType, setNotificationType] = useState( - {} as "success" | "error" - ); - const [notificationTitle, setNotificationTitle] = useState(""); - const [notificationMessage, setNotificationMessage] = useState(""); - - const updateSetup = (value: boolean) => setSetup(value); - const updateFinished = (value: boolean) => setFinished(value); - const updateSavingPopup = (value: boolean) => setSavingPopup(value); - - const updateLocation = (value: string) => setLocation(value); - const updateDistanceUnit = (value: string) => setDistanceUnit(value); - const updateDistance = (value: number) => setDistance(value); - const updateEnds = (value: number) => setEnds(value); - const updateArrowsPerEnd = (value: number) => setArrowsPerEnd(value); - const updateSplitEnds = (value: number) => setSplitEnds(value); - const updateBow = (value: string) => setBow(value); - - const updateData = (value: any) => setData(value); - - const defaultSetup = () => { - setLocation(""); - setDistanceUnit(""); - setDistance(18); - setEnds(10); - setArrowsPerEnd(3); - setSplitEnds(1); - setBow(""); - }; - - useEffect(() => { - if (user) setSignedIn(true); - else setSignedIn(false); - }, [user]); - - const startScoring = () => { - if (!checkIfReady()) return; - - setSetup(false); - }; - - const checkIfReady = () => { - setNotificationType("error"); - setNotificationTitle("Error"); - - if (location === "") { - setNotification(true); - setNotificationMessage("Please select a location"); - return false; - } - - if (distanceUnit === "") { - setNotification(true); - setNotificationMessage("Please select a distance unit"); - return false; - } - - if (bow === "") { - setNotification(true); - setNotificationMessage("Please select a bow"); - return false; - } - - return true; - }; - - const beginSaving = () => { - if (!signedIn) { - setSignUpPopup(true); - return; - } - - updateSavingPopup(true); - }; - - const continueSaving = async ({ - title, - date, - time, - note - }: { - title: string; - date: string; - time: string; - note: string; - }) => { - updateSavingPopup(false); - setNotification(true); - setNotificationType("success"); - setNotificationTitle("Success"); - setNotificationMessage("Session saved!"); - - let cleanDistanceUnit = distanceUnit; - // we are using distance unit from dropdown, but we don't need all the extra info - if (distanceUnit.includes("(")) { - cleanDistanceUnit = distanceUnit.substring(0, distanceUnit.indexOf("(")); - } - cleanDistanceUnit = cleanDistanceUnit.trim(); - cleanDistanceUnit = cleanDistanceUnit.toLowerCase(); - - const sessionInfo = { - location, - distance, - distanceUnit: cleanDistanceUnit, - ends, - arrowsPerEnd, - splitEnds, - bow - }; - let score = data; - const { total } = countTotals({ score }); - const createdAt = date + "," + time; - - await saveSession({ - user, - data, - sessionInfo, - total, - title, - note, - createdAt - }); - - setSetup(true); - }; - - const updateNotification = (value: boolean) => setNotification(value); - return (
- {setup ? ( - - ) : ( -
- - {finished ? : null} - -
- )} - {signUpPopup ? : null} - {savingPopup ? ( - - ) : null} - {notification ? ( - - ) : null} +
+

Archery Scoring (coming soon)

+
); } diff --git a/app/score/layout.tsx b/app/score/layout.tsx new file mode 100644 index 0000000..92e37ea --- /dev/null +++ b/app/score/layout.tsx @@ -0,0 +1,11 @@ +"use client"; + +import { ScoreContextProvider } from "@/lib/context/scoreContext"; + +export default function ScoreLayout({ children }: { children: any }) { + return ( +
+ {children} +
+ ); +} diff --git a/app/score/scoring/page.tsx b/app/score/scoring/page.tsx new file mode 100644 index 0000000..89a1ccf --- /dev/null +++ b/app/score/scoring/page.tsx @@ -0,0 +1,181 @@ +"use client"; + +import NotificationPopup from "@/components/general/notificationPopup"; +import FinalScoringStats from "@/components/scoring/finalScoringStats"; +import SessionOptions from "@/components/scoring/sessionOptions"; +import SaveScorePopup from "@/components/scoring/saveScorePopup"; +import { useScoreContext } from "@/lib/context/scoreContext"; +import ScoringChart from "@/components/scoring/scoringChart"; +import SignUpPopup from "@/components/misc/signUpPopup"; +import { useAuthContext } from "@context/authContext"; +import countTotals from "@/utils/score/countTotals"; +import saveSession from "@/utils/score/saveSession"; +import { useState, useEffect } from "react"; +import { useRouter } from "next/navigation"; + +export default function Scoring() { + const scoreContext = useScoreContext() as any; + const router = useRouter(); + + const [finished, setFinished] = useState(false); + const [savingPopup, setSavingPopup] = useState(false); + const [signedIn, setSignedIn] = useState(false); + const [signUpPopup, setSignUpPopup] = useState(false); + + const { user } = useAuthContext() as { user: any }; + + const [location, setLocation] = useState(""); + const [distanceUnit, setDistanceUnit] = useState(""); + const [distance, setDistance] = useState(0); + const [ends, setEnds] = useState(0); + const [arrowsPerEnd, setArrowsPerEnd] = useState(0); + const [splitEnds, setSplitEnds] = useState(0); + const [bow, setBow] = useState(""); + + const [data, setData] = useState({} as any); + + const [notification, setNotification] = useState(false); + const [notificationType, setNotificationType] = useState( + {} as "success" | "error" + ); + const [notificationTitle, setNotificationTitle] = useState(""); + const [notificationMessage, setNotificationMessage] = useState(""); + + const updateFinished = (value: boolean) => setFinished(value); + const updateSavingPopup = (value: boolean) => setSavingPopup(value); + + const updateData = (value: any) => setData(value); + + useEffect(() => { + setLocation(scoreContext.location); + setDistanceUnit(scoreContext.distanceUnit); + setDistance(scoreContext.distance); + setEnds(scoreContext.ends); + setArrowsPerEnd(scoreContext.arrowsPerEnd); + setSplitEnds(scoreContext.splitEnds); + setBow(scoreContext.bow); + + if (scoreContext.bow === "") { + router.push("/score/setup"); + } + }, []); + + const defaultSetup = () => { + setLocation(""); + setDistanceUnit(""); + setDistance(18); + setEnds(10); + setArrowsPerEnd(3); + setSplitEnds(1); + setBow(""); + }; + + useEffect(() => { + if (user) setSignedIn(true); + else setSignedIn(false); + }, [user]); + + const beginSaving = () => { + if (!signedIn) { + setSignUpPopup(true); + return; + } + + updateSavingPopup(true); + }; + + const continueSaving = async ({ + title, + date, + time, + note + }: { + title: string; + date: string; + time: string; + note: string; + }) => { + updateSavingPopup(false); + setNotification(true); + setNotificationType("success"); + setNotificationTitle("Success"); + setNotificationMessage("Session saved!"); + + let cleanDistanceUnit = distanceUnit; + // we are using distance unit from dropdown, but we don't need all the extra info + if (distanceUnit.includes("(")) { + cleanDistanceUnit = distanceUnit.substring(0, distanceUnit.indexOf("(")); + } + cleanDistanceUnit = cleanDistanceUnit.trim(); + cleanDistanceUnit = cleanDistanceUnit.toLowerCase(); + + const sessionInfo = { + location, + distance, + distanceUnit: cleanDistanceUnit, + ends, + arrowsPerEnd, + splitEnds, + bow + }; + let score = data; + const { total } = countTotals({ score }); + const createdAt = date + "," + time; + + await saveSession({ + user, + data, + sessionInfo, + total, + title, + note, + createdAt + }); + router.push("/score/setup"); + }; + + const updateNotification = (value: boolean) => setNotification(value); + + return ( + <> +
+ + {finished ? : null} + router.push("/score/setup")} + beginSaving={beginSaving} + defaultSetup={defaultSetup} + /> +
+ {signUpPopup ? : null} + {savingPopup ? ( + + ) : null} + {notification ? ( + + ) : null} + + ); +} diff --git a/app/score/setup/page.tsx b/app/score/setup/page.tsx new file mode 100644 index 0000000..b418c93 --- /dev/null +++ b/app/score/setup/page.tsx @@ -0,0 +1,195 @@ +"use client"; + +import NotificationPopup from "@/components/general/notificationPopup"; +import { useScoreContext } from "@/lib/context/scoreContext"; +import { useAuthContext } from "@/lib/context/authContext"; +import getUserDoc from "@/utils/firebase/db/getUserDoc"; +import Dropdown from "@/components/general/dropdown"; +import Button from "@/components/general/button"; +import Slider from "@/components/general/slider"; +import { useRouter } from "next/navigation"; +import { useState, useEffect } from "react"; + +export default function Score() { + const scoreContext = useScoreContext() as any; + const { user } = useAuthContext() as any; + const router = useRouter(); + + const [location, setLocation] = useState(""); + const [distanceUnit, setDistanceUnit] = useState(""); + const [distance, setDistance] = useState(18); + const [ends, setEnds] = useState(10); + const [arrowsPerEnd, setArrowsPerEnd] = useState(3); + const [splitEnds, setSplitEnds] = useState(1); + const [bow, setBow] = useState(""); + + const [notification, setNotification] = useState(false); + const [notificationType, setNotificationType] = useState( + {} as "success" | "error" + ); + const [notificationTitle, setNotificationTitle] = useState(""); + const [notificationMessage, setNotificationMessage] = useState(""); + + const [profiles, setProfiles] = useState([] as any[]); + const [currentProfile, setCurrentProfile] = useState({} as any); + const [profileName, setProfileName] = useState(""); + + const updateLocation = (value: string) => setLocation(value); + const updateDistanceUnit = (value: string) => setDistanceUnit(value); + const updateDistance = (value: number) => setDistance(value); + const updateEnds = (value: number) => setEnds(value); + const updateArrowsPerEnd = (value: number) => setArrowsPerEnd(value); + const updateSplitEnds = (value: number) => setSplitEnds(value); + const updateBow = (value: string) => setBow(value); + + useEffect(() => { + if (user) { + getUserDoc(user).then((doc: any) => { + const dbProfiles = doc.profiles; + const setupProfile = doc.setupDefaultProfile; + setProfiles(dbProfiles); + loadProfile(dbProfiles[setupProfile - 1]); + }); + } + }, [user]); + + const loadProfile = (profile: any) => { + setCurrentProfile(profile); + setLocation(profile.location); + setProfileName(profile.profileName); + setDistanceUnit(profile.distanceUnit); + setDistance(profile.distance); + setEnds(profile.ends); + setArrowsPerEnd(profile.arrowsPerEnd); + setSplitEnds(profile.splitEnds); + setBow(profile.bow); + }; + + const startScoring = () => { + if (!checkIfReady()) return; + + scoreContext.updateLocation(location); + scoreContext.updateDistanceUnit(distanceUnit); + scoreContext.updateDistance(distance); + scoreContext.updateEnds(ends); + scoreContext.updateArrowsPerEnd(arrowsPerEnd); + scoreContext.updateSplitEnds(splitEnds); + scoreContext.updateBow(bow); + + router.push("/score/scoring"); + }; + + const checkIfReady = () => { + setNotificationType("error"); + setNotificationTitle("Error"); + + if (location === "") { + setNotification(true); + setNotificationMessage("Please select a location"); + return false; + } + + if (distanceUnit === "") { + setNotification(true); + setNotificationMessage("Please select a distance unit"); + return false; + } + + if (bow === "") { + setNotification(true); + setNotificationMessage("Please select a bow"); + return false; + } + + return true; + }; + + const updateNotification = (value: boolean) => setNotification(value); + + const switchProfile = (newProfileName: any) => { + profiles.forEach((profile) => { + if (profile.profileName == newProfileName) { + loadProfile(profile); + return; + } + }); + }; + + return ( + <> +
+ +
+ + +
+ + + + + {user ? ( +
+

Scoring Profiles

+ profile.profileName)} + setSelected={setProfileName} + onNewSelection={switchProfile} + /> +
+ ) : null} +
+ {notification ? ( + + ) : null} + + ); +} diff --git a/components/general/button.tsx b/components/general/button.tsx index 3f9a3aa..3fdbb3a 100644 --- a/components/general/button.tsx +++ b/components/general/button.tsx @@ -3,14 +3,16 @@ type Props = { title: string; onClick: (event: any) => void; + disabled?: boolean; selected?: boolean; }; -export default function Button({ title, onClick, selected }: Props) { +export default function Button({ title, onClick, disabled = false, selected }: Props) { return (