From 817b976392532437b175e3fb1f4808b579467663 Mon Sep 17 00:00:00 2001 From: Isaac Lloyd <57055268+theisaaclloyd@users.noreply.github.com> Date: Fri, 18 Apr 2025 23:29:17 -0500 Subject: [PATCH 1/3] added map loading screen --- .../[eventId]/[[...locationId]]/loading.tsx | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 app/event/[mode]/[eventId]/[[...locationId]]/loading.tsx diff --git a/app/event/[mode]/[eventId]/[[...locationId]]/loading.tsx b/app/event/[mode]/[eventId]/[[...locationId]]/loading.tsx new file mode 100644 index 0000000..71ca779 --- /dev/null +++ b/app/event/[mode]/[eventId]/[[...locationId]]/loading.tsx @@ -0,0 +1,76 @@ +"use client"; + +import Heading from "@components/Heading"; +import { useEffect, useState } from "react"; + +const PinpointLoader = () => { + const [progress, setProgress] = useState(0); + const [loadingText, setLoadingText] = useState("Initializing maps"); + + useEffect(() => { + // Loading phrases... + const loadingPhrases = [ + "Initializing maps", + "Loading terrain data", + "Preparing navigation", + "Calculating routes", + "Syncing location data", + "Rendering map tiles", + // "Phoning Brian..." + // "Checking Teams" + // "Waiting for my carmelo" + ]; + + // fake loading progress + const interval = setInterval(() => { + setProgress((prev) => { + if (prev >= 100) { + clearInterval(interval); + return 100; + } + return prev + 1; + }); + + // Change loading text every now and then + if (progress % 16 === 0) { + const nextPhrase = + loadingPhrases[Math.floor((progress / 16) % loadingPhrases.length)]; + setLoadingText(nextPhrase); + } + }, 80); + + return () => clearInterval(interval); + }, [progress]); + + return ( +
+
+ {/* Logo */} +
+ +
+ + {/* Loading progress */} +
+
+
+ {loadingText} +
+ + {progress}% + +
+ +
+
+
+
+
+
+ ); +}; + +export default PinpointLoader; From 3b9842bb33145ca26e7c852d10ab3080fdd95d55 Mon Sep 17 00:00:00 2001 From: Isaac Lloyd <57055268+theisaaclloyd@users.noreply.github.com> Date: Fri, 18 Apr 2025 23:34:53 -0500 Subject: [PATCH 2/3] locations loading spinner --- components/EventSelectForm.tsx | 80 +++++++++++++++++++--------------- 1 file changed, 46 insertions(+), 34 deletions(-) diff --git a/components/EventSelectForm.tsx b/components/EventSelectForm.tsx index 0dbaa4e..34bdce3 100644 --- a/components/EventSelectForm.tsx +++ b/components/EventSelectForm.tsx @@ -10,6 +10,7 @@ import { Select, SelectChangeEvent, Typography, + CircularProgress, } from "@mui/material"; import { Label } from "./ui/label"; import { Plus, Trash } from "lucide-react"; @@ -51,6 +52,7 @@ export default function EventSelectForm({ Location[] >([]); const [locationAdderOpen, setLocationAdderOpen] = useState(false); + const [isLoading, setIsLoading] = useState(false); const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); const [entityToDelete, setEntityToDelete] = useState<{ @@ -89,6 +91,7 @@ export default function EventSelectForm({ const handleChange = async (e: SelectChangeEvent) => { setEventSelected(true); + setIsLoading(true); const selectedEventId = e.target.value; setEventId(selectedEventId); @@ -111,6 +114,7 @@ export default function EventSelectForm({ setSelectedEventLocations(updatedLocations ?? []); } + setIsLoading(false); }; const { data: session } = useSession(); @@ -203,43 +207,51 @@ export default function EventSelectForm({ id="eventLocations" className="space-y-2 rounded-md border-gray-200 border-2 p-2 pt-0 transition-all duration-300 max-h-[45vh] overflow-y-auto" > -
{ - setLocationAdderOpen(true); - }} - > -
- - Add Location + {isLoading ? ( +
+
-
- {selectedEventLocations.map((location) => ( -
{ - // Prevent the click event from triggering when clicking the trash button - if ((e.target as HTMLElement).closest(".trash-button")) - return; - router.push(`/event/edit/${eventId}/${location.id}`); - }} - > -
- {location.name}{" "} + ) : ( + <> +
{ + setLocationAdderOpen(true); + }} + > +
+ + Add Location +
- {canEdit && ( - ( +
{ - e.stopPropagation(); - setDeleteDialogOpen(true); - setEntityToDelete({ entity: location, type: "location" }); + // Prevent the click event from triggering when clicking the trash button + if ((e.target as HTMLElement).closest(".trash-button")) + return; + router.push(`/event/edit/${eventId}/${location.id}`); }} - /> - )} -
- ))} + > +
+ {location.name}{" "} +
+ {canEdit && ( + { + e.stopPropagation(); + setDeleteDialogOpen(true); + setEntityToDelete({ entity: location, type: "location" }); + }} + /> + )} +
+ ))} + + )}
)} @@ -329,4 +341,4 @@ export default function EventSelectForm({ />
); -} +} \ No newline at end of file From fad7d740aaf49753f93a2774a1ab454b61537fda Mon Sep 17 00:00:00 2001 From: Ashlyn DeVries Date: Wed, 23 Apr 2025 14:17:23 -0400 Subject: [PATCH 3/3] minor ui thangs --- components/EventSelectForm.tsx | 41 ++++++++++++------- .../PinpointLoader.tsx | 34 +++++++-------- 2 files changed, 44 insertions(+), 31 deletions(-) rename app/event/[mode]/[eventId]/[[...locationId]]/loading.tsx => components/PinpointLoader.tsx (69%) diff --git a/components/EventSelectForm.tsx b/components/EventSelectForm.tsx index 34bdce3..3ae526f 100644 --- a/components/EventSelectForm.tsx +++ b/components/EventSelectForm.tsx @@ -15,11 +15,11 @@ import { import { Label } from "./ui/label"; import { Plus, Trash } from "lucide-react"; import { Event, Location } from "@prisma/client"; -import LocationAdder from "./LocationCreator"; - import { useSession } from "next-auth/react"; import { useRouter } from "next/navigation"; -import { useState } from "react"; +import { useState, startTransition } from "react"; +import LocationAdder from "./LocationCreator"; +import PinpointLoader from "./PinpointLoader"; import { AlertDialog, @@ -46,24 +46,23 @@ export default function EventSelectForm({ events: Array; }) { const router = useRouter(); + const [eventSelected, setEventSelected] = useState(false); const [eventId, setEventId] = useState(""); - const [selectedEventLocations, setSelectedEventLocations] = useState< - Location[] - >([]); const [locationAdderOpen, setLocationAdderOpen] = useState(false); const [isLoading, setIsLoading] = useState(false); - const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); + const [insertDialogOpen, setInsertDialogOpen] = useState(false); + const [eventToCreate, setEventToCreate] = useState(""); + const [dropdownEvents, setDropdownEvents] = useState(events); + const [isNavigating, setIsNavigating] = useState(false); const [entityToDelete, setEntityToDelete] = useState<{ entity: Event | Location; type: "event" | "location"; }>(); - - const [insertDialogOpen, setInsertDialogOpen] = useState(false); - const [eventToCreate, setEventToCreate] = useState(""); - - const [dropdownEvents, setDropdownEvents] = useState(events); + const [selectedEventLocations, setSelectedEventLocations] = useState< + Location[] + >([]); function deleteEvent(id: string) { DeleteEntity("event", id); @@ -232,7 +231,14 @@ export default function EventSelectForm({ // Prevent the click event from triggering when clicking the trash button if ((e.target as HTMLElement).closest(".trash-button")) return; - router.push(`/event/edit/${eventId}/${location.id}`); + + // Show loading immediately + setIsNavigating(true); + + // Use startTransition for the navigation to indicate it's a UI update + startTransition(() => { + router.push(`/event/edit/${eventId}/${location.id}`); + }); }} >
@@ -244,7 +250,10 @@ export default function EventSelectForm({ onClick={(e) => { e.stopPropagation(); setDeleteDialogOpen(true); - setEntityToDelete({ entity: location, type: "location" }); + setEntityToDelete({ + entity: location, + type: "location", + }); }} /> )} @@ -339,6 +348,8 @@ export default function EventSelectForm({ setSelectedEventLocations((prev) => [...prev, location]); }} /> + + {isNavigating && }
); -} \ No newline at end of file +} diff --git a/app/event/[mode]/[eventId]/[[...locationId]]/loading.tsx b/components/PinpointLoader.tsx similarity index 69% rename from app/event/[mode]/[eventId]/[[...locationId]]/loading.tsx rename to components/PinpointLoader.tsx index 71ca779..1c1aa3c 100644 --- a/app/event/[mode]/[eventId]/[[...locationId]]/loading.tsx +++ b/components/PinpointLoader.tsx @@ -5,7 +5,7 @@ import { useEffect, useState } from "react"; const PinpointLoader = () => { const [progress, setProgress] = useState(0); - const [loadingText, setLoadingText] = useState("Initializing maps"); + const [loadingText, setLoadingText] = useState("Initializing maps..."); useEffect(() => { // Loading phrases... @@ -16,12 +16,9 @@ const PinpointLoader = () => { "Calculating routes", "Syncing location data", "Rendering map tiles", - // "Phoning Brian..." - // "Checking Teams" - // "Waiting for my carmelo" ]; - // fake loading progress + // Fake loading progress const interval = setInterval(() => { setProgress((prev) => { if (prev >= 100) { @@ -33,8 +30,9 @@ const PinpointLoader = () => { // Change loading text every now and then if (progress % 16 === 0) { - const nextPhrase = - loadingPhrases[Math.floor((progress / 16) % loadingPhrases.length)]; + const nextPhrase = `${ + loadingPhrases[Math.floor((progress / 16) % loadingPhrases.length)] + }...`; setLoadingText(nextPhrase); } }, 80); @@ -43,7 +41,12 @@ const PinpointLoader = () => { }, [progress]); return ( -
+
{/* Logo */}
@@ -53,18 +56,17 @@ const PinpointLoader = () => { {/* Loading progress */}
-
- {loadingText} -
- - {progress}% - +
{loadingText}
+ {progress}%