Add trips to UI
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
This commit is contained in:
@@ -134,6 +134,19 @@ export const c2api = {
|
||||
setPreferredToken: (tokenId: string, systemId: string) =>
|
||||
request<{ ok: boolean; preferred_for_system_id: string | null }>(`/tokens/${tokenId}/prefer/${systemId}`, { method: "PUT" }),
|
||||
|
||||
// Trips
|
||||
getTrips: () => request<import("@/lib/types").TripRecord[]>("/trips"),
|
||||
getTrip: (id: string) =>
|
||||
request<import("@/lib/types").TripRecord & { events: import("@/lib/types").TripEvent[] }>(`/trips/${id}`),
|
||||
createTrip: (body: object) =>
|
||||
request<import("@/lib/types").TripRecord>("/trips", { method: "POST", body: JSON.stringify(body) }),
|
||||
deleteTrip: (id: string) =>
|
||||
request(`/trips/${id}`, { method: "DELETE" }),
|
||||
createTripEvent: (tripId: string, body: object) =>
|
||||
request<import("@/lib/types").TripEvent>(`/trips/${tripId}/events`, { method: "POST", body: JSON.stringify(body) }),
|
||||
deleteTripEvent: (tripId: string, eventId: string) =>
|
||||
request(`/trips/${tripId}/events/${eventId}`, { method: "DELETE" }),
|
||||
|
||||
// Per-system AI flag overrides
|
||||
setSystemAiFlags: (systemId: string, flags: { stt_enabled?: boolean | null; correlation_enabled?: boolean | null }) =>
|
||||
request<{ ok: boolean; ai_flags: Record<string, boolean> }>(`/systems/${systemId}/ai-flags`, {
|
||||
|
||||
@@ -98,6 +98,32 @@ export interface AlertRule {
|
||||
created_at?: string;
|
||||
}
|
||||
|
||||
export interface TripEvent {
|
||||
event_id: string;
|
||||
trip_id: string;
|
||||
title: string;
|
||||
date: string;
|
||||
time: string | null;
|
||||
location: string;
|
||||
location_inherited: boolean;
|
||||
maps_link: string | null;
|
||||
notes: string | null;
|
||||
attendees: Record<string, string>;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface TripRecord {
|
||||
trip_id: string;
|
||||
name: string;
|
||||
location: string;
|
||||
maps_link: string | null;
|
||||
start_date: string;
|
||||
end_date: string;
|
||||
attendees: Record<string, string>;
|
||||
created_at: string;
|
||||
events?: TripEvent[];
|
||||
}
|
||||
|
||||
export interface AlertEvent {
|
||||
alert_id: string;
|
||||
rule_id: string;
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
"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 };
|
||||
}
|
||||
Reference in New Issue
Block a user