8edb717dd2
lib/types.ts — TripRecord and TripEvent types lib/c2api.ts — getTrips, getTrip, createTrip, deleteTrip, createTripEvent, deleteTripEvent lib/useTrips.ts — Firestore realtime hook on the trips collection, ordered by start date app/trips/page.tsx — List page split into Upcoming / Past sections, card click navigates to detail, "+ New Trip" modal for admins with all fields including date range and maps link app/trips/[id]/page.tsx — Detail page fetched via C2 API (gets trip + events in one call), day-by-day itinerary with time, location, maps link, notes, and Discord attendees. Add Event modal (date constrained to trip range). Admin-only delete trip + remove event. components/Nav.tsx — Trips link added to the nav
39 lines
1.1 KiB
TypeScript
39 lines
1.1 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
import { collection, onSnapshot, query, orderBy } from "firebase/firestore";
|
|
import { onAuthStateChanged } from "firebase/auth";
|
|
import { db, auth } from "@/lib/firebase";
|
|
import type { TripRecord } from "@/lib/types";
|
|
|
|
export function useTrips() {
|
|
const [trips, setTrips] = useState<TripRecord[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
useEffect(() => {
|
|
let unsubFirestore: (() => void) | undefined;
|
|
|
|
const unsubAuth = onAuthStateChanged(auth, (user) => {
|
|
if (unsubFirestore) { unsubFirestore(); unsubFirestore = undefined; }
|
|
if (!user) { setTrips([]); setLoading(false); return; }
|
|
|
|
const q = query(collection(db, "trips"), orderBy("start_date", "asc"));
|
|
unsubFirestore = onSnapshot(
|
|
q,
|
|
(snap) => {
|
|
setTrips(snap.docs.map((d) => d.data() as TripRecord));
|
|
setLoading(false);
|
|
},
|
|
(err) => {
|
|
console.error("useTrips:", err);
|
|
setLoading(false);
|
|
}
|
|
);
|
|
});
|
|
|
|
return () => { unsubAuth(); if (unsubFirestore) unsubFirestore(); };
|
|
}, []);
|
|
|
|
return { trips, loading };
|
|
}
|