diff --git a/package.json b/package.json index 68ffa747..df1899b0 100644 --- a/package.json +++ b/package.json @@ -36,16 +36,16 @@ "@tanstack/react-query": "^5.66.0", "@types/luxon": "^3.3.2", "@types/node": "^24.1.0", - "@types/react": "19.1.16", - "@types/react-dom": "19.1.9", + "@types/react": "19.2.2", + "@types/react-dom": "19.2.2", "@vercel/analytics": "^1.5.0", "@vercel/speed-insights": "^1.2.0", "autoprefixer": "^10.4.20", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", - "eslint": "9.36.0", - "eslint-config-next": "15.5.4", + "eslint": "9.38.0", + "eslint-config-next": "15.5.6", "firebase": "^12.0.0", "form-data": "^4.0.0", "framer-motion": "^12.0.0", @@ -54,7 +54,7 @@ "lint-staged": "^16.0.0", "lucide-react": "^0.544.0", "luxon": "^3.4.2", - "next": "15.5.4", + "next": "15.5.6", "posthog-js": "^1.257.0", "posthog-node": "^5.5.1", "prettier": "^3.3.3", @@ -69,7 +69,7 @@ "sonner": "^2.0.6", "swiper": "^12.0.0", "tailwind-merge": "^3.3.1", - "tailwindcss": "3.4.17", + "tailwindcss": "3.4.18", "tailwindcss-animate": "^1.0.7", "typescript": "^5.9.2", "zod": "^3.24.2" diff --git a/src/app/(protected)/photos/page.tsx b/src/app/(protected)/photos/page.tsx new file mode 100644 index 00000000..32909903 --- /dev/null +++ b/src/app/(protected)/photos/page.tsx @@ -0,0 +1,243 @@ +"use client"; + +import React, { useState } from "react"; +import PhotoUpload from "@/components/PhotoUpload"; +import { usePhotos } from "@/lib/api/photo"; +import { PHOTO_MILESTONES } from "@/lib/api/photo"; +import { Toaster } from "sonner"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Dialog, DialogContent } from "@/components/ui/dialog"; +import { Camera, Image as ImageIcon, Clock, X, ChevronLeft, ChevronRight } from "lucide-react"; +import { useFirebase } from "@/lib/providers/FirebaseProvider"; +import { Button } from "@/components/ui/button"; + +export default function PhotosPage() { + const { data: photos, isLoading, refetch } = usePhotos(); + const { user } = useFirebase(); + const [selectedImageIndex, setSelectedImageIndex] = useState(null); + const [viewingMyPhotos, setViewingMyPhotos] = useState(false); + + // Separate my photos (all of them) from community photos (only public) + const myPhotos = photos?.filter((photo) => { + const userId = photo.name.split("_")[0]; + return userId === user?.uid; + }) || []; + + const communityPhotos = photos?.filter((photo) => { + const userId = photo.name.split("_")[0]; + const fileType = photo.name.split("_")[1]; + return userId !== user?.uid && fileType === "public"; + }) || []; + + const currentPhotos = viewingMyPhotos ? myPhotos : communityPhotos; + const selectedPhoto = selectedImageIndex !== null ? currentPhotos[selectedImageIndex] : null; + + const openLightbox = (index: number, isMyPhotos: boolean) => { + setSelectedImageIndex(index); + setViewingMyPhotos(isMyPhotos); + }; + + const closeLightbox = () => { + setSelectedImageIndex(null); + }; + + const goToPrevious = () => { + if (selectedImageIndex !== null && selectedImageIndex > 0) { + setSelectedImageIndex(selectedImageIndex - 1); + } + }; + + const goToNext = () => { + if (selectedImageIndex !== null && selectedImageIndex < currentPhotos.length - 1) { + setSelectedImageIndex(selectedImageIndex + 1); + } + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === "ArrowLeft") goToPrevious(); + if (e.key === "ArrowRight") goToNext(); + if (e.key === "Escape") closeLightbox(); + }; + + return ( + <> + +
+
+ {/* Header */} +
+
+

+ Photo Gallery +

+

+ Share your HackPSU experience with the community. Upload photos, browse what others have captured, and relive the highlights of the event. +

+
+
+ + {/* Upload Section */} +
+
+

+ Share a Photo +

+

+ Upload a photo to the public gallery and let everyone see your HackPSU moment. +

+
+ refetch()} /> +
+ + {/* My Photos Section */} + {myPhotos.length > 0 && ( +
+
+

Your Photos

+

+ {myPhotos.length} {myPhotos.length === 1 ? "photo" : "photos"} uploaded +

+
+
+ {myPhotos.map((photo, idx) => ( + + ))} +
+
+ )} + + {/* Community Gallery Section */} +
+
+

+ Community Highlights +

+

+ {isLoading + ? "Loading gallery..." + : communityPhotos.length > 0 + ? `${communityPhotos.length} ${communityPhotos.length === 1 ? "photo" : "photos"} from the HackPSU community` + : "Waiting for the first community photo"} +

+
+ + {isLoading ? ( +
+
+
+

Loading gallery...

+
+
+ ) : communityPhotos.length > 0 ? ( +
+ {communityPhotos.map((photo, idx) => ( + + ))} +
+ ) : ( + + +
+
+ +
+

+ {myPhotos.length > 0 ? "You're the First!" : "Gallery Opening Soon"} +

+

+ {myPhotos.length > 0 + ? "Your photo is the first in the gallery. Others will join soon!" + : "Upload a photo above to start the gallery and inspire others to share their moments."} +

+
+
+
+ )} +
+
+
+ + {/* Lightbox Modal */} + + +
+ {/* Close Button */} + + + {/* Previous Button */} + {selectedImageIndex !== null && selectedImageIndex > 0 && ( + + )} + + {/* Next Button */} + {selectedImageIndex !== null && selectedImageIndex < currentPhotos.length - 1 && ( + + )} + + {/* Image Container */} + {selectedPhoto && ( +
+ {selectedPhoto.name} + {/* Image Counter */} +
+ {selectedImageIndex !== null && `${selectedImageIndex + 1} / ${currentPhotos.length}`} +
+
+ )} +
+
+
+ + ); +} diff --git a/src/app/(protected)/profile/page.tsx b/src/app/(protected)/profile/page.tsx index 2dcf2c4b..fdf52b41 100644 --- a/src/app/(protected)/profile/page.tsx +++ b/src/app/(protected)/profile/page.tsx @@ -39,6 +39,7 @@ import { GraduationCap, HelpCircle, } from "lucide-react"; +import { Roofing, Room } from "@mui/icons-material"; export default function Profile() { const { isAuthenticated, user, logout, isLoading } = useFirebase(); @@ -143,6 +144,10 @@ export default function Profile() { router.push("/team"); }; + const handleReserve = () => { + router.push("/reservation") + } + const handleProject = () => { router.push("/project"); }; @@ -384,6 +389,15 @@ export default function Profile() { Manage Team + ) : ( <> diff --git a/src/app/(protected)/reservation/page.tsx b/src/app/(protected)/reservation/page.tsx new file mode 100644 index 00000000..a0d54d84 --- /dev/null +++ b/src/app/(protected)/reservation/page.tsx @@ -0,0 +1,8 @@ +import ReservationSystem from "@/components/ReservationSystem"; +import React from "react"; + +const ReservationPage = () => { + return ; +}; + +export default ReservationPage; \ No newline at end of file diff --git a/src/components/Navbar/index.tsx b/src/components/Navbar/index.tsx index e1ea340b..8fa5903e 100644 --- a/src/components/Navbar/index.tsx +++ b/src/components/Navbar/index.tsx @@ -171,7 +171,15 @@ const Navbar: React.FC = () => { ? { href: "/profile", text: "Profile" } : { href: "/profile", text: "Register" }; - return [...baseItems, authItem]; + // Only show photos link for authenticated users (keeps homepage clean for guests) + const photosItem = + !isLoading && isAuthenticated + ? { href: "/photos", text: "Photos" } + : null; + + return photosItem + ? [...baseItems, photosItem, authItem] + : [...baseItems, authItem]; }; const navItems = getNavItems(); diff --git a/src/components/PhotoGallery.tsx b/src/components/PhotoGallery.tsx index 4f3f7a93..30b5e042 100644 --- a/src/components/PhotoGallery.tsx +++ b/src/components/PhotoGallery.tsx @@ -10,20 +10,31 @@ import Image from "next/image"; interface CarouselProps { /** Array of image URLs (e.g. ['/images/carousel/1.jpg', '/images/carousel/2.jpg', ...]) */ images: string[]; + /** Variant for different styling - 'default' for home page, 'photos' for photos page */ + variant?: 'default' | 'photos'; } -const PhotoGallery: React.FC = ({ images }) => ( -
- {/* Header */} -
-

- Gallery -

-
-
+const PhotoGallery: React.FC = ({ images, variant = 'default' }) => { + const isPhotosPage = variant === 'photos'; + + return ( +
+ {/* Header */} +
+

+ Gallery +

+
+
= ({ images }) => ( } `}
-); + ); +}; export default PhotoGallery; diff --git a/src/components/PhotoUpload.tsx b/src/components/PhotoUpload.tsx new file mode 100644 index 00000000..1fc19b5e --- /dev/null +++ b/src/components/PhotoUpload.tsx @@ -0,0 +1,330 @@ +"use client"; + +import React, { useRef, useState } from "react"; +import { useUploadPhoto } from "@/lib/api/photo"; +import { PHOTO_MILESTONES } from "@/lib/api/photo"; +import { Button } from "@/components/ui/button"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Card, CardContent } from "@/components/ui/card"; +import { Upload, X, Camera, Loader2 } from "lucide-react"; +import { toast } from "sonner"; +import { Progress } from "@/components/ui/progress"; + +interface FileWithPreview { + file: File; + preview: string; +} + +export default function PhotoUpload({ + onUploaded, +}: { + onUploaded?: () => void; +}) { + const inputRef = useRef(null); + const [files, setFiles] = useState([]); + const [fileType, setFileType] = useState("public"); + const [isDragging, setIsDragging] = useState(false); + const [isUploading, setIsUploading] = useState(false); + const [uploadProgress, setUploadProgress] = useState<{current: number, total: number}>({current: 0, total: 0}); + + const upload = useUploadPhoto(); + + const validateFile = (f: File): boolean => { + // Validate file type + const validImageTypes = [ + "image/jpeg", + "image/jpg", + "image/png", + "image/gif", + "image/webp", + "image/heic", + "image/heif", + ]; + const validVideoTypes = [ + "video/mp4", + "video/quicktime", + "video/x-msvideo", + ]; + + if ( + !validImageTypes.includes(f.type) && + !validVideoTypes.includes(f.type) + ) { + toast.error(`${f.name}: Please upload an image or video file`); + return false; + } + + // Validate file size (100MB) + const maxSize = 100 * 1024 * 1024; + if (f.size > maxSize) { + toast.error(`${f.name}: File size must be less than 100MB`); + return false; + } + + return true; + }; + + const onPick = (fileList: FileList | null) => { + if (!fileList) return; + + const newFiles: FileWithPreview[] = []; + for (let i = 0; i < fileList.length; i++) { + const f = fileList[i]; + if (validateFile(f)) { + newFiles.push({ + file: f, + preview: URL.createObjectURL(f), + }); + } + } + + if (newFiles.length > 0) { + setFiles(prev => [...prev, ...newFiles]); + } + }; + + const handleFile = (e: React.ChangeEvent) => { + onPick(e.target.files); + // Reset input so same files can be selected again + if (inputRef.current) { + inputRef.current.value = ""; + } + }; + + const handleDrop = (e: React.DragEvent) => { + e.preventDefault(); + setIsDragging(false); + onPick(e.dataTransfer.files); + }; + + const removeFile = (index: number) => { + setFiles(prev => { + const newFiles = [...prev]; + // Revoke object URL to prevent memory leak + URL.revokeObjectURL(newFiles[index].preview); + newFiles.splice(index, 1); + return newFiles; + }); + }; + + const startUpload = async () => { + if (files.length === 0 || !fileType) { + toast.error("Please select at least one file and category"); + return; + } + + setIsUploading(true); + setUploadProgress({current: 0, total: files.length}); + + let successCount = 0; + let failCount = 0; + + for (let i = 0; i < files.length; i++) { + try { + await upload.mutateAsync({ file: files[i].file, fileType }); + successCount++; + setUploadProgress({current: i + 1, total: files.length}); + } catch (err: any) { + failCount++; + toast.error(`Failed to upload ${files[i].file.name}: ${err?.message || "Unknown error"}`); + } + } + + // Clean up + files.forEach(f => URL.revokeObjectURL(f.preview)); + setFiles([]); + setFileType("public"); + setIsUploading(false); + setUploadProgress({current: 0, total: 0}); + onUploaded?.(); + + // Show summary toast + if (successCount > 0) { + toast.success(`${successCount} ${successCount === 1 ? "photo" : "photos"} uploaded successfully!`, { + description: "All photos are reviewed by our team before appearing in the gallery. This typically takes a few minutes.", + duration: 6000, + }); + } + + if (failCount > 0) { + toast.error(`${failCount} ${failCount === 1 ? "photo" : "photos"} failed to upload.`); + } + }; + + const selectedMilestone = PHOTO_MILESTONES.find((m) => m.id === fileType); + + return ( + + +
+ {/* Upload Area */} +
e.preventDefault()} + onDragEnter={() => setIsDragging(true)} + onDragLeave={() => setIsDragging(false)} + onDrop={handleDrop} + className={`relative border-2 border-dashed rounded-lg p-8 transition-colors ${ + isDragging + ? "border-primary bg-primary/5" + : "border-muted-foreground/25 hover:border-muted-foreground/50" + }`} + > + + + {files.length > 0 ? ( +
+
+ {files.map((fileWithPreview, idx) => ( +
+ {fileWithPreview.file.type.startsWith("video/") ? ( +
+ ))} +
+
+

+ {files.length} {files.length === 1 ? "file" : "files"} selected +

+ +
+
+ ) : ( +
+ +

+ Upload Photos or Videos +

+

+ Drag and drop your files here, or click to browse +

+ +

+ Supports: JPG, PNG, GIF, WebP, MP4, MOV (max 100MB each) +

+
+ )} +
+ + {/* Milestone Selection */} +
+ + + {selectedMilestone && ( +

+ {selectedMilestone.description} +

+ )} +
+ + {/* Upload Progress */} + {isUploading && ( +
+
+ + Uploading {uploadProgress.current} of {uploadProgress.total} + + + {Math.round((uploadProgress.current / uploadProgress.total) * 100)}% + +
+ +
+ )} + + {/* Upload Button */} +
+ + {files.length > 0 && !isUploading && ( + + )} +
+
+
+
+ ); +} diff --git a/src/components/PrizesChallenges/index.tsx b/src/components/PrizesChallenges/index.tsx index 8fefcc31..2ce798e8 100644 --- a/src/components/PrizesChallenges/index.tsx +++ b/src/components/PrizesChallenges/index.tsx @@ -22,34 +22,32 @@ const AwardBox: React.FC = ({ extra, }) => { return ( -
-
-

+
+
+

{title}

{description && ( -

+

{description}

)} {prizes.length > 0 && ( - - - {prizes.map((prize, index) => ( - - - - - ))} - -
- {prize.place}: - - {prize.amount} -
+
+ {prizes.map((prize, index) => ( +
+
+ {prize.place} +
+
+ {prize.amount} +
+
+ ))} +
)} {extra && ( -
+
{extra}
)} @@ -59,80 +57,70 @@ const AwardBox: React.FC = ({ }; const PrizesChallenges: React.FC = () => { - const { data: prizesAndChallengesFlag } = useFlagState("PrizesEnabled"); + const { data: prizesAndChallengesFlag } = useFlagState("PrizeEnable"); return (
-
+

Prizes & Challenges

-
+
+ {/* {prizesAndChallengesFlag?.isEnabled ? ( */} {prizesAndChallengesFlag?.isEnabled ? ( -
+
- - +
) : (
@@ -210,4 +198,4 @@ const PrizesChallenges: React.FC = () => { ); }; -export default PrizesChallenges; +export default PrizesChallenges; \ No newline at end of file diff --git a/src/components/ReservationSystem/index.tsx b/src/components/ReservationSystem/index.tsx new file mode 100644 index 00000000..8fb1bc9d --- /dev/null +++ b/src/components/ReservationSystem/index.tsx @@ -0,0 +1,874 @@ +"use client"; + +import React, { useState, useMemo } from "react"; +import { ChevronLeft, ChevronRight, Loader2, Calendar, Clock, Users, MapPin, X } from "lucide-react"; +import { + useReservations, + useLocations, + useCreateReservation, + useCancelReservation, +} from "@/lib/api/reservation/hook"; +import { useAllTeams } from "@/lib/api/team/hook"; +import { useActiveHackathonForStatic } from "@/lib/api/hackathon/hook"; +import { useFirebase } from "@/lib/providers/FirebaseProvider"; +import { toast } from "sonner"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; + +interface Room { + id: number; + name: string; + building: string; + floor: string; + capacity: number; +} + +const ReservationSystem: React.FC = () => { + const { user } = useFirebase(); + + // Get user's team first to determine hackathon ID + const { data: teams } = useAllTeams(); + + const userTeam = useMemo(() => { + if (!teams || !user) return null; + return teams.find((team) => + [ + team.member1, + team.member2, + team.member3, + team.member4, + team.member5, + ].includes(user.uid) + ); + }, [teams, user]); + + // Fetch hackathon data to get timeframe - using the active hackathon endpoint + const { data: activeHackathon, isLoading: hackathonLoading } = + useActiveHackathonForStatic(); + + // Extract the hackathon data from the active hackathon response + const hackathon = useMemo(() => { + if (!activeHackathon) return null; + return { + id: activeHackathon.id, + name: activeHackathon.name, + startTime: activeHackathon.startTime, + endTime: activeHackathon.endTime, + active: activeHackathon.active, + }; + }, [activeHackathon]); + + // Initialize selected date based on hackathon start time (normalize to ms) + const initialDate = useMemo(() => { + if (hackathon?.startTime) { + const tsMs = + hackathon.startTime > 9999999999 + ? hackathon.startTime + : hackathon.startTime * 1000; + return new Date(tsMs); + } + return new Date(); + }, [hackathon]); + + const [selectedDate, setSelectedDate] = useState(initialDate); + + // Update selected date when hackathon data loads + React.useEffect(() => { + if ( + hackathon?.startTime && + selectedDate.getTime() !== initialDate.getTime() + ) { + setSelectedDate(initialDate); + } + }, [hackathon, initialDate]); + + // Fetch data using hackathon ID from active hackathon + const { + data: reservations, + isLoading: reservationsLoading, + error: reservationsError, + } = useReservations(hackathon?.id || ""); + const { + data: locations, + isLoading: locationsLoading, + error: locationsError, + } = useLocations(); + const { mutateAsync: createReservation, isPending: isCreating } = + useCreateReservation(); + const { mutateAsync: cancelReservation, isPending: isCanceling } = + useCancelReservation(hackathon?.id || ""); + + const [selectedSlots, setSelectedSlots] = useState<{ + roomId: number; + times: string[]; + } | null>(null); + const [isModalOpen, setIsModalOpen] = useState(false); + const [cancelModalOpen, setCancelModalOpen] = useState(false); + const [reservationToCancel, setReservationToCancel] = useState(null); + + // Get valid date range for the hackathon (normalize to ms) + const dateRange = useMemo(() => { + if (!hackathon) + return { startDate: null, endDate: null, dates: [] as Date[] }; + + const startMs = + hackathon.startTime > 9999999999 + ? hackathon.startTime + : hackathon.startTime * 1000; + const endMs = + hackathon.endTime > 9999999999 + ? hackathon.endTime + : hackathon.endTime * 1000; + + const startDate = new Date(startMs); + const endDate = new Date(endMs); + + const dates: Date[] = []; + const currentDate = new Date(startDate); + + while (currentDate <= endDate) { + dates.push(new Date(currentDate)); + currentDate.setDate(currentDate.getDate() + 1); + } + + return { startDate, endDate, dates }; + }, [hackathon]); + + // Debug + React.useEffect(() => { + console.log("=== Reservation System Debug ==="); + console.log("User:", user?.uid); + console.log("User Team:", userTeam); + console.log("Hackathon ID:", hackathon?.id); + console.log("Hackathon Data:", hackathon); + if (hackathon) { + const startMs = + hackathon.startTime > 9999999999 + ? hackathon.startTime + : hackathon.startTime * 1000; + const endMs = + hackathon.endTime > 9999999999 + ? hackathon.endTime + : hackathon.endTime * 1000; + console.log("Hackathon Start:", new Date(startMs)); + console.log("Hackathon End:", new Date(endMs)); + } + console.log("Selected Date:", selectedDate); + console.log("Date Range:", dateRange); + console.log("Reservations:", reservations); + console.log("Locations:", locations); + console.log("================================"); + }, [ + user, + userTeam, + hackathon, + selectedDate, + dateRange, + reservations, + locations, + ]); + + // Generate time slots based on selected date and hackathon timeframe + const generateTimeSlots = (): string[] => { + if (!hackathon) return []; + + const slots: string[] = []; + const selectedDateStart = new Date(selectedDate); + selectedDateStart.setHours(0, 0, 0, 0); + + const startMs = + hackathon.startTime > 9999999999 + ? hackathon.startTime + : hackathon.startTime * 1000; + const endMs = + hackathon.endTime > 9999999999 + ? hackathon.endTime + : hackathon.endTime * 1000; + + const hackathonStart = new Date(startMs); + const hackathonEnd = new Date(endMs); + + let startHour = 0; + let endHour = 24; // ← include 23:00 (11pm) by default + + // First day: start at hackathon start hour + if (selectedDateStart.toDateString() === hackathonStart.toDateString()) { + startHour = hackathonStart.getHours(); + } + + // Last day: end at hackathon end hour (exclusive) + if (selectedDateStart.toDateString() === hackathonEnd.toDateString()) { + endHour = hackathonEnd.getHours(); + if (endHour === 0) endHour = 24; // if it ends at midnight, show up to 11pm + } + + for (let hour = startHour; hour < endHour; hour++) { + const displayHour = hour === 0 ? 12 : hour > 12 ? hour - 12 : hour; + const period = hour >= 12 ? "pm" : "am"; + slots.push(`${displayHour}:00${period}`); + } + + return slots; + }; + + const timeSlots = generateTimeSlots(); + + const currentDate = selectedDate.toLocaleDateString("en-US", { + weekday: "long", + year: "numeric", + month: "long", + day: "numeric", + }); + + /** + * Helpers: milliseconds everywhere + */ + + // Convert "h:00am/pm" + date -> timestamp in **ms** + const timeToTimestamp = (timeStr: string, date: Date): number => { + const [time, period] = timeStr.split(/(am|pm)/); + let hour = parseInt(time); + if (period === "pm" && hour !== 12) hour += 12; + if (period === "am" && hour === 12) hour = 0; + + const d = new Date(date); + d.setHours(hour, 0, 0, 0); + return d.getTime(); // ms + }; + + // Convert timestamp (sec OR ms) -> "h:00am/pm" + const timestampToTime = (ts: number): string => { + const ms = ts > 9999999999 ? ts : ts * 1000; // normalize to ms + const date = new Date(ms); + const hour = date.getHours(); + const displayHour = hour > 12 ? hour - 12 : hour === 0 ? 12 : hour; + const period = hour >= 12 ? "pm" : "am"; + return `${displayHour}:00${period}`; + }; + + const rooms: Room[] = useMemo(() => { + if (!locations) return []; + return locations + .filter((loc) => loc.capacity !== -1) + .map((loc) => ({ + id: loc.id, + name: loc.name, + building: loc.name.split(" ")[0], + floor: loc.name.match(/\d+/)?.[0]?.charAt(0) || "1", + capacity: loc.capacity, + })); + }, [locations]); + + // Build availability map (normalize incoming reservation times) + const availability = useMemo(() => { + const availabilityMap: { + [key: string]: { available: boolean; reservationId?: string }; + } = {}; + + if (!reservations || !rooms) return availabilityMap; + + // Initialize all slots as available + rooms.forEach((room) => { + timeSlots.forEach((time) => { + availabilityMap[`${room.id}-${time}`] = { available: true }; + }); + }); + + // Mark reserved slots + reservations.forEach((reservation) => { + const startLabel = timestampToTime(reservation.startTime); + const key = `${reservation.locationId}-${startLabel}`; + + const isUserTeamReservation = reservation.teamId === userTeam?.id; + + availabilityMap[key] = { + available: false, + reservationId: isUserTeamReservation ? reservation.id : undefined, + }; + }); + + return availabilityMap; + }, [reservations, rooms, timeSlots, userTeam]); + + const handleSlotClick = async ( + roomId: number, + time: string, + slotInfo: { available: boolean; reservationId?: string } + ) => { + if (!userTeam) { + toast.error("You must be part of a team to make reservations"); + return; + } + + // If clicking an existing reservation by this team, open cancel modal + if (slotInfo.reservationId) { + setReservationToCancel(slotInfo.reservationId); + setSelectedSlots({ roomId, times: [time] }); + setCancelModalOpen(true); + return; + } + + // If slot is not available, do nothing + if (!slotInfo.available) { + toast.error("This time slot is not available"); + return; + } + + // Toggle slot selection + if (selectedSlots?.roomId === roomId) { + const timeIndex = selectedSlots.times.indexOf(time); + if (timeIndex > -1) { + // Deselect this time + const newTimes = selectedSlots.times.filter((t) => t !== time); + if (newTimes.length === 0) { + setSelectedSlots(null); + } else { + setSelectedSlots({ roomId, times: newTimes }); + } + } else { + // Add this time + setSelectedSlots({ roomId, times: [...selectedSlots.times, time].sort() }); + } + } else { + // New room selection + setSelectedSlots({ roomId, times: [time] }); + } + }; + + const handleConfirmReservation = async () => { + if (!selectedSlots || !userTeam || !hackathon) return; + + try { + const hackathonStartMs = + hackathon.startTime > 9999999999 + ? hackathon.startTime + : hackathon.startTime * 1000; + const hackathonEndMs = + hackathon.endTime > 9999999999 + ? hackathon.endTime + : hackathon.endTime * 1000; + + // Sort times to create consecutive reservations + const sortedTimes = [...selectedSlots.times].sort((a, b) => { + const aMs = timeToTimestamp(a, selectedDate); + const bMs = timeToTimestamp(b, selectedDate); + return aMs - bMs; + }); + + // Create reservations for each selected slot + const reservationPromises = sortedTimes.map(async (time) => { + const startTimeMs = timeToTimestamp(time, selectedDate); + const endTimeMs = startTimeMs + 60 * 60 * 1000; // 1 hour + + // Validate within bounds (ms) + if (startTimeMs < hackathonStartMs || endTimeMs > hackathonEndMs) { + throw new Error("Reservation time must be within the hackathon period"); + } + + return createReservation({ + locationId: selectedSlots.roomId, + teamId: userTeam.id, + startTime: startTimeMs, // ms + endTime: endTimeMs, // ms + hackathonId: hackathon.id, + }); + }); + + await Promise.all(reservationPromises); + + toast.success( + `${sortedTimes.length} reservation${sortedTimes.length > 1 ? "s" : ""} created successfully!` + ); + setSelectedSlots(null); + setIsModalOpen(false); + } catch (error: any) { + console.error("Create reservation error:", error); + toast.error(error?.message || "Failed to create reservation"); + } + }; + + const handleCancelReservation = async () => { + if (!reservationToCancel) return; + + try { + await cancelReservation(reservationToCancel); + toast.success("Reservation canceled successfully!"); + setReservationToCancel(null); + setSelectedSlots(null); + setCancelModalOpen(false); + } catch (error) { + console.error("Cancel error:", error); + toast.error("Failed to cancel reservation"); + } + }; + + const handleDateChange = (direction: "prev" | "next") => { + if (!dateRange.dates || dateRange.dates.length === 0) return; + + const currentIndex = dateRange.dates.findIndex( + (d) => d.toDateString() === selectedDate.toDateString() + ); + + if (direction === "next" && currentIndex < dateRange.dates.length - 1) { + setSelectedDate(dateRange.dates[currentIndex + 1]); + setSelectedSlots(null); + } else if (direction === "prev" && currentIndex > 0) { + setSelectedDate(dateRange.dates[currentIndex - 1]); + setSelectedSlots(null); + } + }; + + // Check if we can navigate to prev/next dates + const canNavigate = useMemo(() => { + if (!dateRange.dates || dateRange.dates.length === 0) + return { prev: false, next: false }; + + const currentIndex = dateRange.dates.findIndex( + (d) => d.toDateString() === selectedDate.toDateString() + ); + + return { + prev: currentIndex > 0, + next: currentIndex < dateRange.dates.length - 1, + }; + }, [dateRange.dates, selectedDate]); + + // Show message if user has no team + if (!userTeam && teams && !reservationsLoading) { + return ( +
+
+
No Team Found
+
+ You must be part of a team to make room reservations. +
+
+ Teams available: {teams.length} +
+ +
+
+ ); + } + + if (!teams || reservationsLoading || locationsLoading || hackathonLoading) { + return ( +
+
+ + Loading reservations... +
+ {!teams &&
Loading teams...
} + {hackathonLoading &&
Loading hackathon details...
} + {reservationsLoading &&
Loading reservations...
} + {locationsLoading &&
Loading locations...
} +
+
+
+ ); + } + + if (reservationsError || locationsError) { + return ( +
+
+
+ Error Loading Data +
+
+ {reservationsError && ( +
Reservations Error: {String(reservationsError)}
+ )} + {locationsError && ( +
Locations Error: {String(locationsError)}
+ )} +
+ +
+
+ ); + } + + if (!locations || locations.length === 0) { + return ( +
+
+
No Locations Available
+
+ There are no rooms available for reservation at this time. +
+
+
+ ); + } + + if (!hackathon) { + return ( +
+
+
No Hackathon Data
+
+ Unable to load hackathon timeframe information. +
+
+
+ ); + } + + return ( +
+
+
+

+ Room Reservations +

+ +
+
+ +
+ + + {currentDate} + +
+ +
+ +
+
+
+ Available +
+
+
+ Selected +
+
+
+ Unavailable +
+
+
+ Your Reservation +
+
+
+
+ + {timeSlots.length === 0 ? ( +
+

+ No reservation slots available for this date. +

+
+ ) : ( + <> +
+ {/* Unified Grid Container */} +
+
+ {/* Header Row */} +
+ Space +
+ {timeSlots.map((time) => ( +
+ {time} +
+ ))} + + {/* Data Rows */} + {rooms.map((room, rowIndex) => ( + + {/* Room Name Cell */} +
+ + {room.name} + +
+ + {/* Time Slot Cells */} + {timeSlots.map((time) => { + const slotInfo = availability[`${room.id}-${time}`] || { + available: true, + }; + const isSelected = + selectedSlots?.roomId === room.id && + selectedSlots?.times.includes(time); + const isUserReservation = Boolean( + slotInfo.reservationId + ); + + return ( +
+ handleSlotClick(room.id, time, slotInfo) + } + title={ + isUserReservation + ? `Your reservation at ${room.name} - Click to cancel` + : slotInfo.available + ? isSelected + ? `Deselect ${time}` + : `Select ${time} at ${room.name}` + : "Not available" + } + /> + ); + })} + + ))} +
+
+
+ + {selectedSlots && selectedSlots.times.length > 0 && ( +
+ +
+ )} + + {/* Booking Modal */} + + + + + Confirm Reservation{selectedSlots && selectedSlots.times.length > 1 ? 's' : ''} + + + Review your reservation details before confirming. + + + +
+

+ Note: Reserving a room does not guarantee exclusive use. + Larger rooms are shared hacking spaces. This system ensures your team has a designated spot + and helps manage room capacity to keep everyone comfortable and productive. +

+
+ + {selectedSlots && ( +
+
+ +
+

Room

+

+ {rooms.find((r) => r.id === selectedSlots.roomId)?.name} +

+
+
+ +
+ +
+

Date

+

+ {selectedDate.toLocaleDateString("en-US", { + weekday: "long", + month: "long", + day: "numeric", + })} +

+
+
+ +
+ +
+

+ Time Slots ({selectedSlots.times.length} hour{selectedSlots.times.length > 1 ? 's' : ''}) +

+
+ {selectedSlots.times.map((time) => ( + + {time} + + ))} +
+
+
+ +
+ +
+

Team

+

+ {userTeam?.name || "No Team"} +

+
+
+
+ )} + + + + + +
+
+ + {/* Cancel Modal */} + + + + + Cancel Reservation + + + Are you sure you want to cancel this reservation? + + + + {selectedSlots && ( +
+
+ +
+

Room

+

+ {rooms.find((r) => r.id === selectedSlots.roomId)?.name} +

+
+
+ +
+ +
+

Time

+

+ {selectedDate.toLocaleDateString()} at {selectedSlots.times[0]} +

+
+
+
+ )} + + + + + +
+
+ + )} +
+
+ ); +}; + +export default ReservationSystem; diff --git a/src/components/Schedule/index.tsx b/src/components/Schedule/index.tsx index 30ccf5b0..f35f4c19 100644 --- a/src/components/Schedule/index.tsx +++ b/src/components/Schedule/index.tsx @@ -632,7 +632,7 @@ const Schedule: React.FC = () => { // State to track if component is mounted (client-side const tempScroll = useScroll({ - offset: ["1700px", "2500px"], + offset: ["2200px", "2900px"], }); // pick the real scrollYProgress only after mount diff --git a/src/lib/api/judging/entity.ts b/src/lib/api/judging/entity.ts index 86bcf0a4..76aa446d 100644 --- a/src/lib/api/judging/entity.ts +++ b/src/lib/api/judging/entity.ts @@ -32,9 +32,7 @@ export interface ProjectCreateEntity export interface ProjectPatchEntity extends Partial {} export const PROJECT_CATEGORIES = [ - "Machine Learning", - "Entrepreneurship", - "10th Anniversary: Timeless Tech", + "Beginner Track", ] as const; export type ProjectCategory = (typeof PROJECT_CATEGORIES)[number]; diff --git a/src/lib/api/photo/entity.ts b/src/lib/api/photo/entity.ts new file mode 100644 index 00000000..c9a2eb0b --- /dev/null +++ b/src/lib/api/photo/entity.ts @@ -0,0 +1,112 @@ +export interface PhotoEntity { + name: string; + url: string; + createdAt: string; +} + +export interface PhotoUploadResponse { + photoId: string; + photoUrl: string; +} + +export interface PendingPhoto { + photoId: string; + photoUrl: string; + fileType: string; + uploadedAt: string; +} + +export interface PhotoMilestone { + id: string; + label: string; + description: string; + category: "event" | "food" | "coding" | "social" | "general"; +} + +// Photo milestones for the HackPSU journey +export const PHOTO_MILESTONES: PhotoMilestone[] = [ + { + id: "public", + label: "Public Gallery", + description: "Share with the entire HackPSU community", + category: "general", + }, + { + id: "check-in", + label: "Check-In", + description: "Arrival at HackPSU", + category: "event", + }, + { + id: "team-formation", + label: "Team Formation", + description: "Meeting your team", + category: "social", + }, + { + id: "opening-ceremony", + label: "Opening Ceremony", + description: "Event kickoff", + category: "event", + }, + { + id: "hacking", + label: "Hacking Session", + description: "Building your project", + category: "coding", + }, + { + id: "lunch", + label: "Lunch", + description: "Midday meal", + category: "food", + }, + { + id: "dinner", + label: "Dinner", + description: "Evening meal", + category: "food", + }, + { + id: "midnight-snack", + label: "Midnight Snack", + description: "Late night fuel", + category: "food", + }, + { + id: "workshop", + label: "Workshop", + description: "Learning session", + category: "event", + }, + { + id: "mentoring", + label: "Mentoring", + description: "Getting help from mentors", + category: "social", + }, + { + id: "networking", + label: "Networking", + description: "Meeting sponsors and peers", + category: "social", + }, + { + id: "demo", + label: "Project Demo", + description: "Presenting your work", + category: "event", + }, + { + id: "closing-ceremony", + label: "Closing Ceremony", + description: "Awards and wrap-up", + category: "event", + }, + { + id: "other", + label: "Other Moment", + description: "Any other memorable moment", + category: "general", + }, +]; diff --git a/src/lib/api/photo/hook.ts b/src/lib/api/photo/hook.ts new file mode 100644 index 00000000..2a310a6d --- /dev/null +++ b/src/lib/api/photo/hook.ts @@ -0,0 +1,32 @@ +import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; +import { listPhotos, uploadPhoto } from "./provider"; +import type { PhotoEntity, PhotoUploadResponse } from "./entity"; + +/** + * Hook to fetch all approved photos from the gallery + */ +export function usePhotos() { + return useQuery({ + queryKey: ["photos"], + queryFn: () => listPhotos(), + staleTime: 1000 * 30, // 30 seconds + refetchOnWindowFocus: true, + }); +} + +/** + * Hook to upload a photo with a milestone/reason + */ +export function useUploadPhoto() { + const qc = useQueryClient(); + return useMutation< + PhotoUploadResponse, + Error, + { file: File; fileType: string } + >({ + mutationFn: ({ file, fileType }) => uploadPhoto(file, fileType), + onSuccess: () => { + qc.invalidateQueries({ queryKey: ["photos"] }); + }, + }); +} diff --git a/src/lib/api/photo/index.ts b/src/lib/api/photo/index.ts new file mode 100644 index 00000000..61f2c36f --- /dev/null +++ b/src/lib/api/photo/index.ts @@ -0,0 +1,3 @@ +export * from "./entity"; +export * from "./provider"; +export * from "./hook"; diff --git a/src/lib/api/photo/provider.ts b/src/lib/api/photo/provider.ts new file mode 100644 index 00000000..169badb1 --- /dev/null +++ b/src/lib/api/photo/provider.ts @@ -0,0 +1,32 @@ +import { apiFetch } from "../apiClient"; +import type { PhotoEntity, PhotoUploadResponse } from "./entity"; + +/** + * Get all approved photos from the gallery + * Only returns photos with approvalStatus === "approved" + */ +export async function listPhotos(): Promise { + return apiFetch("/photos", { + method: "GET", + }); +} + +/** + * Upload a photo or video to the gallery + * @param file - The image or video file to upload + * @param fileType - The milestone/reason for the photo (e.g., "check-in", "lunch", "midnight-snack") + * @returns Upload response with photoId and photoUrl + */ +export async function uploadPhoto( + file: File, + fileType: string = "other" +): Promise { + const fd = new FormData(); + fd.append("photo", file); + fd.append("fileType", fileType); + + return apiFetch("/photos/upload", { + method: "POST", + body: fd, + }); +} diff --git a/src/lib/api/reservation/entity.ts b/src/lib/api/reservation/entity.ts new file mode 100644 index 00000000..6b59fb52 --- /dev/null +++ b/src/lib/api/reservation/entity.ts @@ -0,0 +1,28 @@ +export enum ReservationType { + PARTICIPANT = "participant", + ADMIN = "admin", +} + +export interface ReservationEntity { + id: string; + locationId: number; + teamId: string | null; + reservationType: ReservationType; + startTime: number; + endTime: number; + hackathonId: string; +} + +export interface CreateReservationEntity { + locationId: number; + teamId: string; + startTime: number; + endTime: number; + hackathonId: string; +} + +export interface LocationEntity { + id: number; + name: string; + capacity: number; +} diff --git a/src/lib/api/reservation/hook.ts b/src/lib/api/reservation/hook.ts new file mode 100644 index 00000000..d20fa3d1 --- /dev/null +++ b/src/lib/api/reservation/hook.ts @@ -0,0 +1,52 @@ +import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; +import { + getReservations, + createReservation, + cancelReservation, + getLocations, +} from "./provider"; +import { ReservationEntity, CreateReservationEntity } from "./entity"; + +export const reservationQueryKeys = { + all: (hackathonId: string) => ["reservations", hackathonId] as const, + locations: ["locations"] as const, +}; + +export function useReservations(hackathonId: string) { + return useQuery({ + queryKey: reservationQueryKeys.all(hackathonId), + queryFn: () => getReservations(hackathonId), + enabled: Boolean(hackathonId), + }); +} + +export function useLocations() { + return useQuery({ + queryKey: reservationQueryKeys.locations, + queryFn: getLocations, + }); +} + +export function useCreateReservation() { + const qc = useQueryClient(); + return useMutation({ + mutationFn: (data: CreateReservationEntity) => createReservation(data), + onSuccess: (_, variables) => { + qc.invalidateQueries({ + queryKey: reservationQueryKeys.all(variables.hackathonId), + }); + }, + }); +} + +export function useCancelReservation(hackathonId: string) { + const qc = useQueryClient(); + return useMutation({ + mutationFn: (reservationId: string) => cancelReservation(reservationId), + onSuccess: () => { + qc.invalidateQueries({ + queryKey: reservationQueryKeys.all(hackathonId), + }); + }, + }); +} diff --git a/src/lib/api/reservation/index.ts b/src/lib/api/reservation/index.ts new file mode 100644 index 00000000..61f2c36f --- /dev/null +++ b/src/lib/api/reservation/index.ts @@ -0,0 +1,3 @@ +export * from "./entity"; +export * from "./provider"; +export * from "./hook"; diff --git a/src/lib/api/reservation/provider.ts b/src/lib/api/reservation/provider.ts new file mode 100644 index 00000000..3070360b --- /dev/null +++ b/src/lib/api/reservation/provider.ts @@ -0,0 +1,35 @@ +import { apiFetch } from "@/lib/api/apiClient"; +import { + ReservationEntity, + CreateReservationEntity, + LocationEntity, +} from "./entity"; + +export async function getReservations( + hackathonId: string +): Promise { + return apiFetch( + `/reservations?hackathonId=${hackathonId}`, + { method: "GET" } + ); +} + +export async function createReservation( + data: CreateReservationEntity +): Promise { + return apiFetch("/reservations", { + method: "POST", + body: JSON.stringify(data), + headers: { "Content-Type": "application/json" }, + }); +} + +export async function cancelReservation(reservationId: string): Promise { + return apiFetch(`/reservations/${reservationId}`, { + method: "DELETE", + }); +} + +export async function getLocations(): Promise { + return apiFetch("/locations", { method: "GET" }); +} diff --git a/src/lib/providers/FirebaseProvider.tsx b/src/lib/providers/FirebaseProvider.tsx index a126247e..93673f1e 100644 --- a/src/lib/providers/FirebaseProvider.tsx +++ b/src/lib/providers/FirebaseProvider.tsx @@ -14,6 +14,12 @@ import { type Auth, type User, signOut } from "firebase/auth"; import { auth } from "@/lib/config/firebase"; import posthog from "posthog-js"; +// Helper function to get auth service URL from environment +function getAuthServiceURL(): string { + // Use environment variable if set, otherwise default to production + return process.env.NEXT_PUBLIC_AUTH_SERVICE_URL || "https://auth.hackpsu.org"; +} + type FirebaseContextType = { auth: Auth; isLoading: boolean; @@ -37,7 +43,7 @@ export const FirebaseProvider: FC = ({ children }) => { const [hasInitialized, setHasInitialized] = useState(false); const [isLoggingOut, setIsLoggingOut] = useState(false); - // Verify session with the auth server + // Verify session with the auth server (environment-aware) const verifySession = useCallback(async () => { // Don't verify session if we're in the middle of logging out if (isLoggingOut) { @@ -45,9 +51,11 @@ export const FirebaseProvider: FC = ({ children }) => { return; } - console.log("Verifying session..."); + const authServiceURL = getAuthServiceURL(); + console.log("Verifying session with:", authServiceURL); + try { - const response = await fetch("https://auth.hackpsu.org/api/sessionUser", { + const response = await fetch(`${authServiceURL}/api/sessionUser`, { method: "GET", credentials: "include", headers: { @@ -57,6 +65,14 @@ export const FirebaseProvider: FC = ({ children }) => { console.log("Session verification response:", response.status); + if (response.status === 401) { + // No session or session invalid - redirect to login + console.log("No valid session, redirecting to login"); + const currentUrl = encodeURIComponent(window.location.href); + window.location.href = `${authServiceURL}/login?returnTo=${currentUrl}`; + return; + } + if (!response.ok) { throw new Error(`Session verification failed: ${response.status}`); } @@ -110,6 +126,8 @@ export const FirebaseProvider: FC = ({ children }) => { console.log("Initial session check successful"); } catch (err) { console.log("No valid session found or timeout occurred:", err); + // Don't redirect here on initial load - let the user stay on the page + // Only redirect when explicitly calling verifySession } finally { setIsLoading(false); setHasInitialized(true); @@ -119,20 +137,22 @@ export const FirebaseProvider: FC = ({ children }) => { checkSession(); }, [verifySession, hasInitialized, isLoggingOut]); - // Enhanced logout function + // Enhanced logout function (environment-aware) const logout = useCallback(async () => { console.log("Starting logout process..."); setIsLoggingOut(true); setError(undefined); setIsLoading(true); + const authServiceURL = getAuthServiceURL(); + try { // Clear PostHog identity posthog.reset(); // Clear the session on the auth server first console.log("Clearing auth server session..."); - await fetch("https://auth.hackpsu.org/api/sessionLogout", { + await fetch(`${authServiceURL}/api/sessionLogout`, { method: "POST", credentials: "include", headers: { @@ -184,4 +204,4 @@ export const useFirebase = () => { const ctx = useContext(FirebaseContext); if (!ctx) throw new Error("useFirebase must be used within FirebaseProvider"); return ctx; -}; +}; \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 7cea636b..c9260d45 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1111,24 +1111,26 @@ resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== -"@eslint/config-array@^0.21.0": - version "0.21.0" - resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.21.0.tgz#abdbcbd16b124c638081766392a4d6b509f72636" - integrity sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ== +"@eslint/config-array@^0.21.1": + version "0.21.1" + resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.21.1.tgz#7d1b0060fea407f8301e932492ba8c18aff29713" + integrity sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA== dependencies: - "@eslint/object-schema" "^2.1.6" + "@eslint/object-schema" "^2.1.7" debug "^4.3.1" minimatch "^3.1.2" -"@eslint/config-helpers@^0.3.1": - version "0.3.1" - resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.3.1.tgz#d316e47905bd0a1a931fa50e669b9af4104d1617" - integrity sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA== +"@eslint/config-helpers@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.4.1.tgz#7d173a1a35fe256f0989a0fdd8d911ebbbf50037" + integrity sha512-csZAzkNhsgwb0I/UAV6/RGFTbiakPCf0ZrGmrIxQpYvGZ00PhTkSnyKNolphgIvmnJeGw6rcGVEXfTzUnFuEvw== + dependencies: + "@eslint/core" "^0.16.0" -"@eslint/core@^0.15.2": - version "0.15.2" - resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.15.2.tgz#59386327d7862cc3603ebc7c78159d2dcc4a868f" - integrity sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg== +"@eslint/core@^0.16.0": + version "0.16.0" + resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.16.0.tgz#490254f275ba9667ddbab344f4f0a6b7a7bd7209" + integrity sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q== dependencies: "@types/json-schema" "^7.0.15" @@ -1147,22 +1149,22 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@9.36.0": - version "9.36.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.36.0.tgz#b1a3893dd6ce2defed5fd49de805ba40368e8fef" - integrity sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw== +"@eslint/js@9.38.0": + version "9.38.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.38.0.tgz#f7aa9c7577577f53302c1d795643589d7709ebd1" + integrity sha512-UZ1VpFvXf9J06YG9xQBdnzU+kthors6KjhMAl6f4gH4usHyh31rUf2DLGInT8RFYIReYXNSydgPY0V2LuWgl7A== -"@eslint/object-schema@^2.1.6": - version "2.1.6" - resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.6.tgz#58369ab5b5b3ca117880c0f6c0b0f32f6950f24f" - integrity sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA== +"@eslint/object-schema@^2.1.7": + version "2.1.7" + resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.7.tgz#6e2126a1347e86a4dedf8706ec67ff8e107ebbad" + integrity sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA== -"@eslint/plugin-kit@^0.3.5": - version "0.3.5" - resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz#fd8764f0ee79c8ddab4da65460c641cefee017c5" - integrity sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w== +"@eslint/plugin-kit@^0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.4.0.tgz#f6a245b42886abf6fc9c7ab7744a932250335ab2" + integrity sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A== dependencies: - "@eslint/core" "^0.15.2" + "@eslint/core" "^0.16.0" levn "^0.4.1" "@firebase/ai@2.3.0": @@ -1975,62 +1977,62 @@ "@emnapi/runtime" "^1.5.0" "@tybys/wasm-util" "^0.10.1" -"@next/env@15.5.4": - version "15.5.4" - resolved "https://registry.yarnpkg.com/@next/env/-/env-15.5.4.tgz#1d4aa6b238662d9cd95aea356b149b6f73061f95" - integrity sha512-27SQhYp5QryzIT5uO8hq99C69eLQ7qkzkDPsk3N+GuS2XgOgoYEeOav7Pf8Tn4drECOVDsDg8oj+/DVy8qQL2A== +"@next/env@15.5.6": + version "15.5.6" + resolved "https://registry.yarnpkg.com/@next/env/-/env-15.5.6.tgz#7009d88d419a36a4ba9e110c151604444744a74d" + integrity sha512-3qBGRW+sCGzgbpc5TS1a0p7eNxnOarGVQhZxfvTdnV0gFI61lX7QNtQ4V1TSREctXzYn5NetbUsLvyqwLFJM6Q== -"@next/eslint-plugin-next@15.5.4": - version "15.5.4" - resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-15.5.4.tgz#e7af86b7197a26e8a9d3784d1e365a2eb9298f5b" - integrity sha512-SR1vhXNNg16T4zffhJ4TS7Xn7eq4NfKfcOsRwea7RIAHrjRpI9ALYbamqIJqkAhowLlERffiwk0FMvTLNdnVtw== +"@next/eslint-plugin-next@15.5.6": + version "15.5.6" + resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-15.5.6.tgz#8d08f11cb98a8401ff64746cb3035948ae1d1f61" + integrity sha512-YxDvsT2fwy1j5gMqk3ppXlsgDopHnkM4BoxSVASbvvgh5zgsK8lvWerDzPip8k3WVzsTZ1O7A7si1KNfN4OZfQ== dependencies: fast-glob "3.3.1" -"@next/swc-darwin-arm64@15.5.4": - version "15.5.4" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.4.tgz#80cba1bec831d4b01fd03cbc48dfb7050775e5ee" - integrity sha512-nopqz+Ov6uvorej8ndRX6HlxCYWCO3AHLfKK2TYvxoSB2scETOcfm/HSS3piPqc3A+MUgyHoqE6je4wnkjfrOA== - -"@next/swc-darwin-x64@15.5.4": - version "15.5.4" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.4.tgz#d5408b19298f40da2b3dc9c2f9d1063ad98bd626" - integrity sha512-QOTCFq8b09ghfjRJKfb68kU9k2K+2wsC4A67psOiMn849K9ZXgCSRQr0oVHfmKnoqCbEmQWG1f2h1T2vtJJ9mA== - -"@next/swc-linux-arm64-gnu@15.5.4": - version "15.5.4" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.4.tgz#3b6b389bb4a1c9728a14afbbd59d2366ccd80b55" - integrity sha512-eRD5zkts6jS3VfE/J0Kt1VxdFqTnMc3QgO5lFE5GKN3KDI/uUpSyK3CjQHmfEkYR4wCOl0R0XrsjpxfWEA++XA== - -"@next/swc-linux-arm64-musl@15.5.4": - version "15.5.4" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.4.tgz#956127ecdfd56cda535af4651eed72a3b7270971" - integrity sha512-TOK7iTxmXFc45UrtKqWdZ1shfxuL4tnVAOuuJK4S88rX3oyVV4ZkLjtMT85wQkfBrOOvU55aLty+MV8xmcJR8A== - -"@next/swc-linux-x64-gnu@15.5.4": - version "15.5.4" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.4.tgz#9386de65e86c0b34ef19e14f0ffbd4328a08d5e6" - integrity sha512-7HKolaj+481FSW/5lL0BcTkA4Ueam9SPYWyN/ib/WGAFZf0DGAN8frNpNZYFHtM4ZstrHZS3LY3vrwlIQfsiMA== - -"@next/swc-linux-x64-musl@15.5.4": - version "15.5.4" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.4.tgz#c9094e5479b58c89d35b465f165b69be68de5a75" - integrity sha512-nlQQ6nfgN0nCO/KuyEUwwOdwQIGjOs4WNMjEUtpIQJPR2NUfmGpW2wkJln1d4nJ7oUzd1g4GivH5GoEPBgfsdw== - -"@next/swc-win32-arm64-msvc@15.5.4": - version "15.5.4" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.4.tgz#e83ca6b5ce9499bde5a4f3351cf74dc9e92cc83e" - integrity sha512-PcR2bN7FlM32XM6eumklmyWLLbu2vs+D7nJX8OAIoWy69Kef8mfiN4e8TUv2KohprwifdpFKPzIP1njuCjD0YA== - -"@next/swc-win32-x64-msvc@15.5.4": - version "15.5.4" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.4.tgz#5b5baf1bcb0ecba70d1768a0c8be59dfdcb2f111" - integrity sha512-1ur2tSHZj8Px/KMAthmuI9FMp/YFusMMGoRNJaRZMOlSkgvLjzosSdQI0cJAKogdHl3qXUQKL9MGaYvKwA7DXg== +"@next/swc-darwin-arm64@15.5.6": + version "15.5.6" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.6.tgz#f80d3fe536f29f3217ca07d031f7b43862234059" + integrity sha512-ES3nRz7N+L5Umz4KoGfZ4XX6gwHplwPhioVRc25+QNsDa7RtUF/z8wJcbuQ2Tffm5RZwuN2A063eapoJ1u4nPg== + +"@next/swc-darwin-x64@15.5.6": + version "15.5.6" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.6.tgz#289334478617318a0d8d9f1f6661a15952f4e4ab" + integrity sha512-JIGcytAyk9LQp2/nuVZPAtj8uaJ/zZhsKOASTjxDug0SPU9LAM3wy6nPU735M1OqacR4U20LHVF5v5Wnl9ptTA== + +"@next/swc-linux-arm64-gnu@15.5.6": + version "15.5.6" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.6.tgz#efdd993cd9ad88b82c948c8e518e045566dd2f98" + integrity sha512-qvz4SVKQ0P3/Im9zcS2RmfFL/UCQnsJKJwQSkissbngnB/12c6bZTCB0gHTexz1s6d/mD0+egPKXAIRFVS7hQg== + +"@next/swc-linux-arm64-musl@15.5.6": + version "15.5.6" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.6.tgz#a45185126faf69eb65a017bd2c015ad7e86f5c84" + integrity sha512-FsbGVw3SJz1hZlvnWD+T6GFgV9/NYDeLTNQB2MXoPN5u9VA9OEDy6fJEfePfsUKAhJufFbZLgp0cPxMuV6SV0w== + +"@next/swc-linux-x64-gnu@15.5.6": + version "15.5.6" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.6.tgz#8174dc8e03a1f7df292bead360f83c53f8dd8b73" + integrity sha512-3QnHGFWlnvAgyxFxt2Ny8PTpXtQD7kVEeaFat5oPAHHI192WKYB+VIKZijtHLGdBBvc16tiAkPTDmQNOQ0dyrA== + +"@next/swc-linux-x64-musl@15.5.6": + version "15.5.6" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.6.tgz#6d0776d81c5bd6a1780e6c39f32d7ef172900635" + integrity sha512-OsGX148sL+TqMK9YFaPFPoIaJKbFJJxFzkXZljIgA9hjMjdruKht6xDCEv1HLtlLNfkx3c5w2GLKhj7veBQizQ== + +"@next/swc-win32-arm64-msvc@15.5.6": + version "15.5.6" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.6.tgz#4b63d511b5c41278a48168fccb89cf00912411af" + integrity sha512-ONOMrqWxdzXDJNh2n60H6gGyKed42Ieu6UTVPZteXpuKbLZTH4G4eBMsr5qWgOBA+s7F+uB4OJbZnrkEDnZ5Fg== + +"@next/swc-win32-x64-msvc@15.5.6": + version "15.5.6" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.6.tgz#9ed5e84bd85009625dd35c444668d13061452a50" + integrity sha512-pxK4VIjFRx1MY92UycLOOw7dTdvccWsNETQ0kDHkBlcFH1GrTLUjSiHU1ohrznnux6TqRHgv5oflhfIWZwVROQ== "@next/third-parties@^15.3.1": - version "15.5.4" - resolved "https://registry.yarnpkg.com/@next/third-parties/-/third-parties-15.5.4.tgz#a6eb17336d707d92ca88709939f0439cf0841fa0" - integrity sha512-l3T1M/EA32phPzZx+gkQAWOF3E5iAULL1nX4Ej0JZQOXaBwwJzb/rd2uefr5TAshJj/+HjjwmdFu7olXudvgVg== + version "15.5.6" + resolved "https://registry.yarnpkg.com/@next/third-parties/-/third-parties-15.5.6.tgz#6b7f49702e969331b69c3105902c9f443d496c2c" + integrity sha512-B1BLvEi7edGERNN0njxpiqbqkp3zAZ69eJ5C0vwj/XINRzcC25b9MCqxbSHq094d306H65UnlhEkBv+a8c74iA== dependencies: third-party-capital "1.0.20" @@ -2167,12 +2169,12 @@ resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== -"@posthog/cli@^0.4.7": - version "0.4.8" - resolved "https://registry.yarnpkg.com/@posthog/cli/-/cli-0.4.8.tgz#d6d6ec14bfbde4a637785ffae9059d55ec3ea333" - integrity sha512-LdPMD9vppl8TOMeG+sYsKDgs3dK8cA8vvgG65i0DrXlCPt2+lpRINtQJ+M7Bm8VZFUpzMieNOl+/o7axwxwXlw== +"@posthog/cli@^0.5.1": + version "0.5.1" + resolved "https://registry.yarnpkg.com/@posthog/cli/-/cli-0.5.1.tgz#a7f2110466bcec78738d9c69b34cec463b0303f2" + integrity sha512-1a3zcLyP7fJp+IJ+jmodZ9poaQwGXJuz1/N9vEquudHqXi8cnhPy4OrbBoY3y1wZN4doYTwGqbfbts0662OUEw== dependencies: - axios "^1.10.0" + axios "^1.11.0" axios-proxy-builder "^0.1.2" console.table "^0.10.0" detect-libc "^2.0.4" @@ -2183,12 +2185,18 @@ resolved "https://registry.yarnpkg.com/@posthog/core/-/core-1.2.2.tgz#63382e2e208f501b2c22246ccaae55052f77ac1d" integrity sha512-f16Ozx6LIigRG+HsJdt+7kgSxZTHeX5f1JlCGKI1lXcvlZgfsCR338FuMI2QRYXGl+jg/vYFzGOTQBxl90lnBg== +"@posthog/core@1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@posthog/core/-/core-1.3.0.tgz#59f11c10485f61bba45c89d53554e41c7f6c9e3e" + integrity sha512-hxLL8kZNHH098geedcxCz8y6xojkNYbmJEW+1vFXsmPcExyCXIUUJ/34X6xa9GcprKxd0Wsx3vfJQLQX4iVPhw== + "@posthog/nextjs-config@^1.0.2": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@posthog/nextjs-config/-/nextjs-config-1.3.1.tgz#cc934e18cb5327cd8176cfe85b4ef3d907577bb8" - integrity sha512-0CKzXX7Oh9tgFdqTQMq9FUMDIBhu0td9NYsyU20y+pu4meh9xSCcJI7GIcwxRTBoDIJvR//YU98niMlnu+uObQ== + version "1.3.4" + resolved "https://registry.yarnpkg.com/@posthog/nextjs-config/-/nextjs-config-1.3.4.tgz#2b1a80cbdba09ecbde10502000be54cf8a56632d" + integrity sha512-cf1XIkK28y/5qL/as/IutH0EwysWy1gKdGM/xETZ0l25jTTN9wASx1ucP/BeE3dOYAVF039zlU9WNCFbIxW4Uw== dependencies: - "@posthog/cli" "^0.4.7" + "@posthog/cli" "^0.5.1" + "@posthog/core" "1.3.0" semver "^7.7.2" "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": @@ -2790,17 +2798,17 @@ dependencies: mini-svg-data-uri "^1.2.3" -"@tanstack/query-core@5.90.2": - version "5.90.2" - resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.90.2.tgz#ac5d0d0f19a38071db2d21d758b5c35a85d9c1d8" - integrity sha512-k/TcR3YalnzibscALLwxeiLUub6jN5EDLwKDiO7q5f4ICEoptJ+n9+7vcEFy5/x/i6Q+Lb/tXrsKCggf5uQJXQ== +"@tanstack/query-core@5.90.5": + version "5.90.5" + resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.90.5.tgz#0175f9f517514906db8ab379589ed3f96694ecc4" + integrity sha512-wLamYp7FaDq6ZnNehypKI5fNvxHPfTYylE0m/ZpuuzJfJqhR5Pxg9gvGBHZx4n7J+V5Rg5mZxHHTlv25Zt5u+w== "@tanstack/react-query@^5.66.0": - version "5.90.2" - resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.90.2.tgz#2f045931b7d44bef02c5261fedba75ef1a418726" - integrity sha512-CLABiR+h5PYfOWr/z+vWFt5VsOA2ekQeRQBFSKlcoW6Ndx/f8rfyVmq4LbgOM4GG2qtxAxjLYLOpCNTYm4uKzw== + version "5.90.5" + resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.90.5.tgz#545e61282c787bd87ac5785da9a4943462f78ef6" + integrity sha512-pN+8UWpxZkEJ/Rnnj2v2Sxpx1WFlaa9L6a4UO89p6tTQbeo+m0MS8oYDjbggrR8QcTyjKoYWKS3xJQGr3ExT8Q== dependencies: - "@tanstack/query-core" "5.90.2" + "@tanstack/query-core" "5.90.5" "@tanstack/react-virtual@^3.13.9": version "3.13.12" @@ -2854,11 +2862,11 @@ integrity sha512-H3iskjFIAn5SlJU7OuxUmTEpebK6TKB8rxZShDslBMZJ5u9S//KM1sbdAisiSrqwLQncVjnpi2OK2J51h+4lsg== "@types/node@>=12.12.47", "@types/node@>=13.7.0", "@types/node@^24.1.0": - version "24.6.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-24.6.1.tgz#29cd365beb4419b3b8271c7464f1a563446d7481" - integrity sha512-ljvjjs3DNXummeIaooB4cLBKg2U6SPI6Hjra/9rRIy7CpM0HpLtG9HptkMKAb4HYWy5S7HUvJEuWgr/y0U8SHw== + version "24.8.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-24.8.1.tgz#74c8ae00b045a0a351f2837ec00f25dfed0053be" + integrity sha512-alv65KGRadQVfVcG69MuB4IzdYVpRwMG/mq8KWOaoOdyY617P5ivaDiMCGOFDWD2sAn5Q0mR3mRtUOgm99hL9Q== dependencies: - undici-types "~7.13.0" + undici-types "~7.14.0" "@types/parse-json@^4.0.0": version "4.0.2" @@ -2870,20 +2878,20 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.15.tgz#e6e5a86d602beaca71ce5163fadf5f95d70931c7" integrity sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw== -"@types/react-dom@19.1.9": - version "19.1.9" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-19.1.9.tgz#5ab695fce1e804184767932365ae6569c11b4b4b" - integrity sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ== +"@types/react-dom@19.2.2": + version "19.2.2" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-19.2.2.tgz#a4cc874797b7ddc9cb180ef0d5dc23f596fc2332" + integrity sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw== "@types/react-transition-group@^4.4.12": version "4.4.12" resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.12.tgz#b5d76568485b02a307238270bfe96cb51ee2a044" integrity sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w== -"@types/react@19.1.16": - version "19.1.16" - resolved "https://registry.yarnpkg.com/@types/react/-/react-19.1.16.tgz#6329b1c17a5de624439eae673f86a2c5569a42be" - integrity sha512-WBM/nDbEZmDUORKnh5i1bTnAz6vTohUf9b8esSMu+b24+srbaxa04UbJgWx78CVfNXA20sNu0odEIluZDFdCog== +"@types/react@19.2.2": + version "19.2.2" + resolved "https://registry.yarnpkg.com/@types/react/-/react-19.2.2.tgz#ba123a75d4c2a51158697160a4ea2ff70aa6bf36" + integrity sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA== dependencies: csstype "^3.0.2" @@ -3254,7 +3262,7 @@ axios-proxy-builder@^0.1.2: dependencies: tunnel "^0.0.6" -axios@^1.10.0: +axios@^1.11.0: version "1.12.2" resolved "https://registry.yarnpkg.com/axios/-/axios-1.12.2.tgz#6c307390136cf7a2278d09cec63b136dfc6e6da7" integrity sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw== @@ -3562,9 +3570,9 @@ core-js-compat@^3.40.0: browserslist "^4.24.4" core-js@^3.38.1: - version "3.45.1" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.45.1.tgz#5810e04a1b4e9bc5ddaa4dd12e702ff67300634d" - integrity sha512-L4NPsJlCfZsPeXukyzHFlg/i7IIVwHSItR0wg0FLNqYClJ4MQYTYLbC7EkjKYRLZF2iof2MUgN0EGy7MdQFChg== + version "3.46.0" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.46.0.tgz#323a092b96381a9184d0cd49ee9083b2f93373bb" + integrity sha512-vDMm9B0xnqqZ8uSBpZ8sNtRtOdmfShrvT6h2TuQGLs0Is+cR0DYbj/KWP6ALVNbWPpqA/qPLoOuppJN07humpA== cosmiconfig@^7.0.0: version "7.1.0" @@ -3743,7 +3751,12 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== -detect-libc@^2.0.4, detect-libc@^2.1.0: +detect-libc@^2.0.4: + version "2.1.2" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.1.2.tgz#689c5dcdc1900ef5583a4cb9f6d7b473742074ad" + integrity sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ== + +detect-libc@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.1.0.tgz#3ca811f60a7b504b0480e5008adacc660b0b8c4f" integrity sha512-vEtk+OcP7VBRtQZ1EJ3bdgzSfBjgnEalLTp5zjJrS+2Z1w2KZly4SBdac/WDU3hhsNAZ9E8SC96ME4Ey8MZ7cg== @@ -3848,9 +3861,9 @@ electron-to-chromium@^1.5.73: integrity sha512-Lv65Btwv9W4J9pyODI6EWpdnhfvrve/us5h1WspW8B2Fb0366REPtY3hX7ounk1CkV/TBjWCEvCBBbYbmV0qCA== emoji-regex@^10.3.0: - version "10.5.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.5.0.tgz#be23498b9e39db476226d8e81e467f39aca26b78" - integrity sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg== + version "10.6.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.6.0.tgz#bf3d6e8f7f8fd22a65d9703475bc0147357a6b0d" + integrity sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A== emoji-regex@^8.0.0: version "8.0.0" @@ -4011,12 +4024,12 @@ escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -eslint-config-next@15.5.4: - version "15.5.4" - resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-15.5.4.tgz#b15f15aa9030098fc5a48ea5bb3c2bba6a531f70" - integrity sha512-BzgVVuT3kfJes8i2GHenC1SRJ+W3BTML11lAOYFOOPzrk2xp66jBOAGEFRw+3LkYCln5UzvFsLhojrshb5Zfaw== +eslint-config-next@15.5.6: + version "15.5.6" + resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-15.5.6.tgz#e054f4f76d866b37848c1a142726e08c9a3a66c0" + integrity sha512-cGr3VQlPsZBEv8rtYp4BpG1KNXDqGvPo9VC1iaCgIA11OfziC/vczng+TnAS3WpRIR3Q5ye/6yl+CRUuZ1fPGg== dependencies: - "@next/eslint-plugin-next" "15.5.4" + "@next/eslint-plugin-next" "15.5.6" "@rushstack/eslint-patch" "^1.10.3" "@typescript-eslint/eslint-plugin" "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0" "@typescript-eslint/parser" "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0" @@ -4154,24 +4167,23 @@ eslint-visitor-keys@^4.2.1: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz#4cfea60fe7dd0ad8e816e1ed026c1d5251b512c1" integrity sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ== -eslint@9.36.0: - version "9.36.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.36.0.tgz#9cc5cbbfb9c01070425d9bfed81b4e79a1c09088" - integrity sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ== +eslint@9.38.0: + version "9.38.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.38.0.tgz#3957d2af804e5cf6cc503c618f60acc71acb2e7e" + integrity sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw== dependencies: "@eslint-community/eslint-utils" "^4.8.0" "@eslint-community/regexpp" "^4.12.1" - "@eslint/config-array" "^0.21.0" - "@eslint/config-helpers" "^0.3.1" - "@eslint/core" "^0.15.2" + "@eslint/config-array" "^0.21.1" + "@eslint/config-helpers" "^0.4.1" + "@eslint/core" "^0.16.0" "@eslint/eslintrc" "^3.3.1" - "@eslint/js" "9.36.0" - "@eslint/plugin-kit" "^0.3.5" + "@eslint/js" "9.38.0" + "@eslint/plugin-kit" "^0.4.0" "@humanfs/node" "^0.16.6" "@humanwhocodes/module-importer" "^1.0.1" "@humanwhocodes/retry" "^0.4.2" "@types/estree" "^1.0.6" - "@types/json-schema" "^7.0.15" ajv "^6.12.4" chalk "^4.0.0" cross-spawn "^7.0.6" @@ -4433,11 +4445,11 @@ fraction.js@^4.3.7: integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew== framer-motion@^12.0.0: - version "12.23.22" - resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-12.23.22.tgz#798333fdcc5cd65fabfc450985b7236bbdbf5d64" - integrity sha512-ZgGvdxXCw55ZYvhoZChTlG6pUuehecgvEAJz0BHoC5pQKW1EC5xf1Mul1ej5+ai+pVY0pylyFfdl45qnM1/GsA== + version "12.23.24" + resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-12.23.24.tgz#4895b67e880bd2b1089e61fbaa32ae802fc24b8c" + integrity sha512-HMi5HRoRCTou+3fb3h9oTLyJGBxHfW+HnNE25tAXOvVx/IvwMHK0cx7IR4a2ZU6sh3IX1Z+4ts32PcYBOqka8w== dependencies: - motion-dom "^12.23.21" + motion-dom "^12.23.23" motion-utils "^12.23.6" tslib "^2.4.0" @@ -4941,7 +4953,7 @@ jackspeak@^4.1.1: dependencies: "@isaacs/cliui" "^8.0.2" -jiti@^1.21.6: +jiti@^1.21.7: version "1.21.7" resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.7.tgz#9dd81043424a3d28458b193d965f0d18a2300ba9" integrity sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A== @@ -5065,7 +5077,7 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" -lilconfig@^3.0.0, lilconfig@^3.1.3: +lilconfig@^3.1.1, lilconfig@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.1.3.tgz#a1bcfd6257f9585bf5ae14ceeebb7b559025e4c4" integrity sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw== @@ -5076,14 +5088,14 @@ lines-and-columns@^1.1.6: integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== lint-staged@^16.0.0: - version "16.2.3" - resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-16.2.3.tgz#790866221d75602510507b5be40b2c7963715960" - integrity sha512-1OnJEESB9zZqsp61XHH2fvpS1es3hRCxMplF/AJUDa8Ho8VrscYDIuxGrj3m8KPXbcWZ8fT9XTMUhEQmOVKpKw== + version "16.2.4" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-16.2.4.tgz#1f166370e32d9b7eb10583e86d86e1117f7ab489" + integrity sha512-Pkyr/wd90oAyXk98i/2KwfkIhoYQUMtss769FIT9hFM5ogYZwrk+GRE46yKXSg2ZGhcJ1p38Gf5gmI5Ohjg2yg== dependencies: commander "^14.0.1" listr2 "^9.0.4" micromatch "^4.0.8" - nano-spawn "^1.0.3" + nano-spawn "^2.0.0" pidtree "^0.6.0" string-argv "^0.3.2" yaml "^2.8.1" @@ -5173,9 +5185,9 @@ lru-cache@^10.2.0: integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== lru-cache@^11.0.0: - version "11.2.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.2.1.tgz#d426ac471521729c6c1acda5f7a633eadaa28db2" - integrity sha512-r8LA6i4LP4EeWOhqBaZZjDWwehd1xUJPCJd9Sv300H0ZmcUER4+JPh7bqqZeqs1o5pgtgvXm+d9UGrB5zZGDiQ== + version "11.2.2" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.2.2.tgz#40fd37edffcfae4b2940379c0722dc6eeaa75f24" + integrity sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg== lru-cache@^5.1.1: version "5.1.1" @@ -5282,10 +5294,10 @@ minimist@^1.2.0, minimist@^1.2.6, minimist@^1.2.8: resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== -motion-dom@^12.23.21: - version "12.23.21" - resolved "https://registry.yarnpkg.com/motion-dom/-/motion-dom-12.23.21.tgz#1efe2d1bdda31875f0ee56d06f02be7b562a0bdd" - integrity sha512-5xDXx/AbhrfgsQmSE7YESMn4Dpo6x5/DTZ4Iyy4xqDvVHWvFVoV+V2Ri2S/ksx+D40wrZ7gPYiMWshkdoqNgNQ== +motion-dom@^12.23.23: + version "12.23.23" + resolved "https://registry.yarnpkg.com/motion-dom/-/motion-dom-12.23.23.tgz#8f874333ea1a04ee3a89eb928f518b463d589e0e" + integrity sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA== dependencies: motion-utils "^12.23.6" @@ -5308,10 +5320,10 @@ mz@^2.7.0: object-assign "^4.0.1" thenify-all "^1.0.0" -nano-spawn@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/nano-spawn/-/nano-spawn-1.0.3.tgz#ef8d89a275eebc8657e67b95fc312a6527a05b8d" - integrity sha512-jtpsQDetTnvS2Ts1fiRdci5rx0VYws5jGyC+4IYOTnIQ/wwdf6JdomlHBwqC3bJYOvaKu0C2GSZ1A60anrYpaA== +nano-spawn@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/nano-spawn/-/nano-spawn-2.0.0.tgz#f1250434c09ae18870d4f729fc54b406cf85a3e1" + integrity sha512-tacvGzUY5o2D8CBh2rrwxyNojUsZNU2zjNTzKQrkgGJQTbGAfArVWXSKMBokBeeg6C7OLRGUEyoFlYbfeWQIqw== nanoid@^3.1.23, nanoid@^3.3.6, nanoid@^3.3.8: version "3.3.11" @@ -5328,25 +5340,25 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -next@15.5.4: - version "15.5.4" - resolved "https://registry.yarnpkg.com/next/-/next-15.5.4.tgz#e7412c805c0b686ceaf294de703b7c9be59a4081" - integrity sha512-xH4Yjhb82sFYQfY3vbkJfgSDgXvBB6a8xPs9i35k6oZJRoQRihZH+4s9Yo2qsWpzBmZ3lPXaJ2KPXLfkvW4LnA== +next@15.5.6: + version "15.5.6" + resolved "https://registry.yarnpkg.com/next/-/next-15.5.6.tgz#16d9d1e9ba2e8caf82ba15e060a12286cd25db30" + integrity sha512-zTxsnI3LQo3c9HSdSf91O1jMNsEzIXDShXd4wVdg9y5shwLqBXi4ZtUUJyB86KGVSJLZx0PFONvO54aheGX8QQ== dependencies: - "@next/env" "15.5.4" + "@next/env" "15.5.6" "@swc/helpers" "0.5.15" caniuse-lite "^1.0.30001579" postcss "8.4.31" styled-jsx "5.1.6" optionalDependencies: - "@next/swc-darwin-arm64" "15.5.4" - "@next/swc-darwin-x64" "15.5.4" - "@next/swc-linux-arm64-gnu" "15.5.4" - "@next/swc-linux-arm64-musl" "15.5.4" - "@next/swc-linux-x64-gnu" "15.5.4" - "@next/swc-linux-x64-musl" "15.5.4" - "@next/swc-win32-arm64-msvc" "15.5.4" - "@next/swc-win32-x64-msvc" "15.5.4" + "@next/swc-darwin-arm64" "15.5.6" + "@next/swc-darwin-x64" "15.5.6" + "@next/swc-linux-arm64-gnu" "15.5.6" + "@next/swc-linux-arm64-musl" "15.5.6" + "@next/swc-linux-x64-gnu" "15.5.6" + "@next/swc-linux-x64-musl" "15.5.6" + "@next/swc-win32-arm64-msvc" "15.5.6" + "@next/swc-win32-x64-msvc" "15.5.6" sharp "^0.34.3" no-case@^3.0.4: @@ -5628,13 +5640,12 @@ postcss-js@^4.0.1: dependencies: camelcase-css "^2.0.1" -postcss-load-config@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-4.0.2.tgz#7159dcf626118d33e299f485d6afe4aff7c4a3e3" - integrity sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ== +"postcss-load-config@^4.0.2 || ^5.0 || ^6.0": + version "6.0.1" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-6.0.1.tgz#6fd7dcd8ae89badcf1b2d644489cbabf83aa8096" + integrity sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g== dependencies: - lilconfig "^3.0.0" - yaml "^2.3.4" + lilconfig "^3.1.1" postcss-nested@^6.2.0: version "6.2.0" @@ -5675,11 +5686,11 @@ postcss@^8.4.47: source-map-js "^1.2.1" posthog-js@^1.257.0: - version "1.268.9" - resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.268.9.tgz#08cced88984b14353ca12f2a7313bdd40b3158f3" - integrity sha512-ejK5/i0TUQ8I1SzaIn7xWNf5TzOjWquawpgjKit8DyucD3Z1yf7LTMtgCYZN8oRx9VjiPcP34fSk8YsWQmmkTQ== + version "1.276.0" + resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.276.0.tgz#3605e99dd42e69b772fc43c661c064edc3693aa1" + integrity sha512-FYZE1037LrAoKKeUU0pUL7u8WwNK2BVeg5TFApwquVPUdj9h7u5Z077A313hPN19Ar+7Y+VHxqYqdHc4VNsVgw== dependencies: - "@posthog/core" "1.2.2" + "@posthog/core" "1.3.0" core-js "^3.38.1" fflate "^0.4.8" preact "^10.19.3" @@ -5765,11 +5776,11 @@ react-card-flip@^1.2.3: integrity sha512-yb8+yyeTf5UVlZ/FC78XDgxYeWhgA0W28OXNB31LjiFeY1Y2kFhLP7bFiED4KNaRWKjaafT74G38XU53a6eIuw== react-dom@^19.0.0: - version "19.1.1" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.1.1.tgz#2daa9ff7f3ae384aeb30e76d5ee38c046dc89893" - integrity sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw== + version "19.2.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.2.0.tgz#00ed1e959c365e9a9d48f8918377465466ec3af8" + integrity sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ== dependencies: - scheduler "^0.26.0" + scheduler "^0.27.0" react-hook-form@^7.54.2: version "7.63.0" @@ -5854,9 +5865,9 @@ react-transition-group@^4.4.5: prop-types "^15.6.2" react@^19.0.0: - version "19.1.1" - resolved "https://registry.yarnpkg.com/react/-/react-19.1.1.tgz#06d9149ec5e083a67f9a1e39ce97b06a03b644af" - integrity sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ== + version "19.2.0" + resolved "https://registry.yarnpkg.com/react/-/react-19.2.0.tgz#d33dd1721698f4376ae57a54098cb47fc75d93a5" + integrity sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ== read-cache@^1.0.0: version "1.0.0" @@ -6067,10 +6078,10 @@ safe-regex-test@^1.0.3, safe-regex-test@^1.1.0: es-errors "^1.3.0" is-regex "^1.2.1" -scheduler@^0.26.0: - version "0.26.0" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.26.0.tgz#4ce8a8c2a2095f13ea11bf9a445be50c555d6337" - integrity sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA== +scheduler@^0.27.0: + version "0.27.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.27.0.tgz#0c4ef82d67d1e5c1e359e8fc76d3a87f045fe5bd" + integrity sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q== semver@^6.3.1: version "6.3.1" @@ -6083,9 +6094,9 @@ semver@^7.6.0, semver@^7.6.3: integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA== semver@^7.7.2: - version "7.7.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58" - integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== + version "7.7.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946" + integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q== set-function-length@^1.2.2: version "1.2.2" @@ -6481,10 +6492,10 @@ tailwindcss-animate@^1.0.7: resolved "https://registry.yarnpkg.com/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz#318b692c4c42676cc9e67b19b78775742388bef4" integrity sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA== -tailwindcss@3.4.17: - version "3.4.17" - resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.17.tgz#ae8406c0f96696a631c790768ff319d46d5e5a63" - integrity sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og== +tailwindcss@3.4.18: + version "3.4.18" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.18.tgz#9fa9650aace186644b608242f1e57d2d55593301" + integrity sha512-6A2rnmW5xZMdw11LYjhcI5846rt9pbLSabY5XPxo+XWdxwZaFEn47Go4NzFiHu9sNNmr/kXivP1vStfvMaK1GQ== dependencies: "@alloc/quick-lru" "^5.2.0" arg "^5.0.2" @@ -6494,7 +6505,7 @@ tailwindcss@3.4.17: fast-glob "^3.3.2" glob-parent "^6.0.2" is-glob "^4.0.3" - jiti "^1.21.6" + jiti "^1.21.7" lilconfig "^3.1.3" micromatch "^4.0.8" normalize-path "^3.0.0" @@ -6503,7 +6514,7 @@ tailwindcss@3.4.17: postcss "^8.4.47" postcss-import "^15.1.0" postcss-js "^4.0.1" - postcss-load-config "^4.0.2" + postcss-load-config "^4.0.2 || ^5.0 || ^6.0" postcss-nested "^6.2.0" postcss-selector-parser "^6.1.2" resolve "^1.22.8" @@ -6655,10 +6666,10 @@ unbox-primitive@^1.1.0: has-symbols "^1.1.0" which-boxed-primitive "^1.1.1" -undici-types@~7.13.0: - version "7.13.0" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.13.0.tgz#a20ba7c0a2be0c97bd55c308069d29d167466bff" - integrity sha512-Ov2Rr9Sx+fRgagJ5AX0qvItZG/JKKoBRAVITs1zk7IqZGTJUwgUr7qoYBpWwakpWilTZFM98rG/AFRocu10iIQ== +undici-types@~7.14.0: + version "7.14.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.14.0.tgz#4c037b32ca4d7d62fae042174604341588bc0840" + integrity sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA== unicode-canonical-property-names-ecmascript@^2.0.0: version "2.0.1" @@ -6870,11 +6881,6 @@ yaml@^1.10.0: resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== -yaml@^2.3.4: - version "2.7.0" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.7.0.tgz#aef9bb617a64c937a9a748803786ad8d3ffe1e98" - integrity sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA== - yaml@^2.8.1: version "2.8.1" resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.8.1.tgz#1870aa02b631f7e8328b93f8bc574fac5d6c4d79"