From d8e540fe27b622a77dbe18c1aec144c4dab31197 Mon Sep 17 00:00:00 2001 From: Gizmotronn Date: Tue, 26 Nov 2024 11:56:25 +1100 Subject: [PATCH] =?UTF-8?q?=F0=9F=91=98=F0=9F=92=A6=20=E2=86=9D=20[SSG-76]?= =?UTF-8?q?:=20Updating=20travel=20styling=20&=20fetching=20functionality?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/scenes/travel/page.tsx | 40 ++- components/(scenes)/travel/BoardingPass.tsx | 84 ++++++ .../(scenes)/travel/BoardingPassCard.tsx | 38 +++ .../(scenes)/travel/BookingConfirmation.tsx | 109 +++++++ components/(scenes)/travel/DepartureList.tsx | 32 ++ .../(scenes)/travel/DepartureSelector.tsx | 101 +++++++ .../(scenes)/travel/DestinationList.tsx | 55 ++++ components/(scenes)/travel/PlanetSelector.tsx | 285 ++++++++++++++++++ components/(scenes)/travel/SolarSystem.tsx | 19 +- types/Travel.ts | 36 +++ 10 files changed, 778 insertions(+), 21 deletions(-) create mode 100644 components/(scenes)/travel/BoardingPass.tsx create mode 100644 components/(scenes)/travel/BoardingPassCard.tsx create mode 100644 components/(scenes)/travel/BookingConfirmation.tsx create mode 100644 components/(scenes)/travel/DepartureList.tsx create mode 100644 components/(scenes)/travel/DepartureSelector.tsx create mode 100644 components/(scenes)/travel/DestinationList.tsx create mode 100644 components/(scenes)/travel/PlanetSelector.tsx create mode 100644 types/Travel.ts diff --git a/app/scenes/travel/page.tsx b/app/scenes/travel/page.tsx index 58bd6e22..fe1263c1 100644 --- a/app/scenes/travel/page.tsx +++ b/app/scenes/travel/page.tsx @@ -6,12 +6,36 @@ import { useActivePlanet } from "@/context/ActivePlanet"; import SwitchPlanet from "@/components/(scenes)/travel/SolarSystem"; import StarnetLayout from "@/components/Layout/Starnet"; -export default function Travel() { +import PlanetSelector from "@/components/(scenes)/travel/PlanetSelector"; + +export default function Home() { + const session = useSession(); + + const mockUser = { + id: "1", + name: session?.user?.id || "Guest", + avatar: "/placeholder.svg?height=40&width=40", + frequentFlyerNumber: "SF123456", + frequentFlyerStatus: "Gold" as "Gold", + }; + return ( - -
-
-
-
- ) -}; \ No newline at end of file +
+ console.log("Selected planet:", planet.name)} + /> +
+ ); +}; + + +// export default function Travel() { +// return ( +// +//
+// {/*
*/} +//
+//
+// ) +// }; \ No newline at end of file diff --git a/components/(scenes)/travel/BoardingPass.tsx b/components/(scenes)/travel/BoardingPass.tsx new file mode 100644 index 00000000..b44764e5 --- /dev/null +++ b/components/(scenes)/travel/BoardingPass.tsx @@ -0,0 +1,84 @@ +import React from 'react' +import { motion } from 'framer-motion' +import { User, Planet } from "@/types/Travel"; +import Image from 'next/image' +import { Button } from '@/components/ui/button' +import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '@/components/ui/card' + +interface BoardingPassProps { + user: User + planet: Planet + onBeginTrip: () => void + onCancelBooking: () => void +}; + +function QRCodePattern() { + const size = 10 + const cells = Array(size * size).fill(0).map(() => Math.random() > 0.5) + + return ( +
+
+ {cells.map((filled, i) => ( +
+ ))} +
+
+ ); +}; + +export default function BoardingPass({ user, planet, onBeginTrip, onCancelBooking }: BoardingPassProps) { + return ( + + + + Boarding Pass + + +
+
+ Passenger + {user.name} +
+
+ Destination + {planet.name} +
+
+ Station + {planet.station} +
+
+ Temperature + {planet.temperature}°C +
+
+
+
+ +
+
+
+ + + + +
+
+ ); +}; \ No newline at end of file diff --git a/components/(scenes)/travel/BoardingPassCard.tsx b/components/(scenes)/travel/BoardingPassCard.tsx new file mode 100644 index 00000000..0ead8ef7 --- /dev/null +++ b/components/(scenes)/travel/BoardingPassCard.tsx @@ -0,0 +1,38 @@ +import React from 'react'; +import { BoardingPass } from "@/types/Travel"; +import Image from 'next/image'; + +interface BoardingPassCardProps { + boardingPass: BoardingPass; +} + +const BoardingPassCard: React.FC = ({ boardingPass }) => { + return ( +
+

Boarding Pass

+
+
+

Passenger Information

+

Name: {boardingPass.userName}

+

Frequent Flyer: {boardingPass.frequentFlyerNumber}

+

Status: {boardingPass.frequentFlyerStatus}

+
+
+

Flight Information

+

From: {boardingPass.departurePlanet} ({boardingPass.departureTemperature}°C)

+

To: {boardingPass.destinationPlanet} ({boardingPass.destinationTemperature}°C)

+

Rocket: {boardingPass.rocketType}

+

Departure: {new Date(boardingPass.departureTime).toLocaleString()}

+
+
+
+ QR Code +
+
+ Barcode +
+
+ ); +}; + +export default BoardingPassCard; \ No newline at end of file diff --git a/components/(scenes)/travel/BookingConfirmation.tsx b/components/(scenes)/travel/BookingConfirmation.tsx new file mode 100644 index 00000000..3cf16838 --- /dev/null +++ b/components/(scenes)/travel/BookingConfirmation.tsx @@ -0,0 +1,109 @@ +'use client' + +import { motion } from 'framer-motion' +import { ChevronLeft } from 'lucide-react' +import { Button } from '@/components/ui/button' +import { Card } from '@/components/ui/card' +import Image from 'next/image' +import { Planet, Departure, User } from '@/types/Travel' + +interface BookingConfirmationProps { + planet: Planet + departure: Departure + user: User + onBack: () => void +}; + +function QRCodePattern() { + const size = 10 + const cells = Array(size * size).fill(0).map(() => Math.random() > 0.5) + + return ( +
+
+ {cells.map((filled, i) => ( +
+ ))} +
+
+ ); +}; + +export default function BookingConfirmation({ planet, departure, user, onBack }: BookingConfirmationProps) { + return ( +
+
+ +

Booking Confirmation

+
+ + + +
+
+

{planet.name}

+

{planet.station}

+
+ {planet.name} +
+ +
+
+ Passenger + {user.name + } +
+
+ Departure + {new Date(departure.departureTime).toLocaleString()}, Gate {departure.gate} +
+
+ Duration + {departure.duration} +
+
+ Rocket + {departure.rocketType} +
+
+ +
+
+
+ Total Price +

${departure.price}

+
+
+ Booking Reference +

{planet.name.substring(0, 3).toUpperCase()}-{Math.random().toString(36).substring(2, 7).toUpperCase()}

+
+
+
+
+ +
+ +
+ + +
+
+ ); +}; \ No newline at end of file diff --git a/components/(scenes)/travel/DepartureList.tsx b/components/(scenes)/travel/DepartureList.tsx new file mode 100644 index 00000000..fd3f1ba9 --- /dev/null +++ b/components/(scenes)/travel/DepartureList.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import { Departure } from '@/types/Travel'; + +interface DepartureListProps { + departures: Departure[]; + onSelect: (departure: Departure) => void; + selectedDeparture: Departure | null; +} + +const DepartureList: React.FC = ({ departures, onSelect, selectedDeparture }) => { + return ( +
+

Select Your Departure

+
    + {departures.map(departure => ( +
  • onSelect(departure)} + > + {departure.rocketType} - Departs at{' '} + {new Date(departure.departureTime).toLocaleString()} +
  • + ))} +
+
+ ); +}; + +export default DepartureList; \ No newline at end of file diff --git a/components/(scenes)/travel/DepartureSelector.tsx b/components/(scenes)/travel/DepartureSelector.tsx new file mode 100644 index 00000000..e6197254 --- /dev/null +++ b/components/(scenes)/travel/DepartureSelector.tsx @@ -0,0 +1,101 @@ +'use client' + +import { motion } from 'framer-motion' +import { ChevronLeft, Rocket, Clock, CreditCard } from 'lucide-react' +import { Button } from '@/components/ui/button' +import { Card } from '@/components/ui/card' +import { Planet, Departure } from '@/types/Travel' + +interface DepartureSelectorProps { + planet: Planet + onSelect: (departure: Departure) => void + onBack: () => void +} + +const departures: Departure[] = [ + { + id: '1', + rocketType: 'Falcon Heavy', + departureTime: '2023-07-01T09:00:00Z', + price: 2499, + duration: '6 months', + gate: 'A1' + }, + { + id: '2', + rocketType: 'Starship', + departureTime: '2023-07-02T14:30:00Z', + price: 3999, + duration: '4 months', + gate: 'B3' + }, + { + id: '3', + rocketType: 'New Glenn', + departureTime: '2023-07-03T20:45:00Z', + price: 2999, + duration: '5 months', + gate: 'C2' + } +] + +export default function DepartureSelector({ planet, onSelect, onBack }: DepartureSelectorProps) { + return ( +
+
+ +

Select Departure to {planet.name}

+
+ +
+ {departures.map((departure, index) => ( + + onSelect(departure)} + > +
+
+

{departure.rocketType}

+

Gate {departure.gate}

+
+
+ ${departure.price} +

per person

+
+
+
+
+ + {new Date(departure.departureTime).toLocaleString()} +
+
+ + {departure.duration} +
+
+
+
+ ))} +
+ +
+ +
+
+ ); +}; \ No newline at end of file diff --git a/components/(scenes)/travel/DestinationList.tsx b/components/(scenes)/travel/DestinationList.tsx new file mode 100644 index 00000000..ca843536 --- /dev/null +++ b/components/(scenes)/travel/DestinationList.tsx @@ -0,0 +1,55 @@ +import React from 'react'; +import { Destination } from '@/types/Travel'; + +interface DestinationListProps { + destinations: Destination[]; + onSelect: (destination: Destination) => void; + selectedDestination: Destination | null; +} + +const DestinationList: React.FC = ({ destinations, onSelect, selectedDestination }) => { + const solarDestinations = destinations.filter(d => d.type === 'solar'); + const exoplanets = destinations.filter(d => d.type === 'exoplanet'); + + return ( +
+

Select Your Destination

+
+
+

Solar System

+
    + {solarDestinations.map(destination => ( +
  • onSelect(destination)} + > + {destination.name} +
  • + ))} +
+
+
+

Exoplanets

+
    + {exoplanets.map(destination => ( +
  • onSelect(destination)} + > + {destination.name} +
  • + ))} +
+
+
+
+ ); +}; + +export default DestinationList; \ No newline at end of file diff --git a/components/(scenes)/travel/PlanetSelector.tsx b/components/(scenes)/travel/PlanetSelector.tsx new file mode 100644 index 00000000..14ec6d52 --- /dev/null +++ b/components/(scenes)/travel/PlanetSelector.tsx @@ -0,0 +1,285 @@ +'use client' + +import { useEffect, useState } from 'react' +import { motion } from 'framer-motion' +import { User, Planet } from '@/types/Travel'; +import Image from 'next/image'; +import { ChevronRight } from 'lucide-react'; +import BoardingPass from './BoardingPass'; +import { useSession, useSupabaseClient } from '@supabase/auth-helpers-react'; + +interface PlanetSelectorProps { + user: User + onSelect: (planet: Planet) => void +}; + +const initialPlanets: Planet[] = [ + { + id: 'mars', + name: 'MARS', + station: 'Olympus Base', + image: '/placeholder.svg?height=400&width=400', + color: '#FF4D4D', + temperature: -63, + type: 'intra-solar', + planetType: 'Terrestrial', + description: 'The Red Planet, fourth from the Sun.', + orbitPosition: 0, + available: true + }, + { + id: 'moon', + name: 'MOON', + station: 'Tranquility Base', + image: '/placeholder.svg?height=400&width=400', + color: '#F7F5E9', + temperature: -20, + planetType: 'Terrestrial', + type: 'intra-solar', + description: 'Earth\'s only natural satellite.', + orbitPosition: 1, + available: true + }, + { + id: 'europa', + name: 'EUROPA', + station: 'Galileo Station', + image: '/placeholder.svg?height=400&width=400', + color: '#5FCBC3', + temperature: -160, + planetType: "Frozen", + type: 'intra-solar', + description: 'Jupiter\'s icy moon with a subsurface ocean.', + orbitPosition: 2, + available: false + }, + { + id: 'venus', + name: 'VENUS', + station: 'Cloud City', + image: '/placeholder.svg?height=400&width=400', + color: '#FFD700', + temperature: 462, + planetType: "Hellish", + type: 'intra-solar', + description: 'The hottest planet in our solar system.', + orbitPosition: 3, + available: false + }, +] + +const getParentStarColor = (type: 'intra-solar' | 'interstellar') => { + if (type === 'intra-solar') { + return 'bg-gradient-radial from-yellow-400 to-orange-500'; + } + const hue = Math.floor(Math.random() * 60); + return `bg-gradient-radial from-[hsl(${hue},100%,50%)] to-[hsl(${hue},100%,30%)]`; +}; + +export default function PlanetSelector({ user, onSelect }: PlanetSelectorProps) { + const supabase = useSupabaseClient(); + const session = useSession(); + + const [planets, setPlanets] = useState([...initialPlanets]); // Use renamed constant here + const [selectedPlanet, setSelectedPlanet] = useState(initialPlanets[0]); + const [selectedType, setSelectedType] = useState<'intra-solar' | 'interstellar'>('intra-solar'); + const [isBooked, setIsBooked] = useState(false); + + useEffect(() => { + const fetchExoplanets = async () => { + try { + const userId = session?.user?.id; // Ensure `session.user.id` is available + if (!userId) { + console.error('User is not logged in'); + return; + } + + // Fetch classifications first + const { data: classifications, error: classificationsError } = await supabase + .from('classifications') + .select('anomaly') + .eq('author', userId) + .neq('anomaly', null); + + if (classificationsError) throw classificationsError; + + // Extract anomaly IDs + const anomalyIds = classifications.map((classification) => classification.anomaly); + + if (anomalyIds.length === 0) { + console.warn('No anomalies found for the user.'); + return; + } + + // Fetch anomalies using the anomaly IDs + const { data, error } = await supabase + .from('anomalies') + .select(` + id, + content, + anomalytype, + avatar_url, + anomalySet + `) + .in('id', anomalyIds) + .eq('anomalySet', 'tess'); + + if (error) throw error; + + // Map data to Planet objects + const exoplanetsData: Planet[] = data.map((exoplanet) => ({ + id: exoplanet.id as string, + name: exoplanet.content || 'Unknown Name', + color: '#5FCBC3', + station: 'Unknown Station', + temperature: NaN, // No temperature in the query + planetType: exoplanet.anomalytype || 'Unknown', + type: 'interstellar', + description: exoplanet.content || 'No description available', + image: exoplanet.avatar_url || '/assets/Planets/DefaultExoplanet.png', + orbitPosition: Math.floor(Math.random() * 5), + available: true, + })); + + setPlanets((prevPlanets) => [...prevPlanets, ...exoplanetsData]); + } catch (error) { + console.error('Error fetching exoplanets:', error); + } + }; + + + fetchExoplanets(); + }, [supabase, session]); + + const filteredPlanets = planets.filter((planet) => planet.type === selectedType); + + const handlePlanetSelect = (planet: Planet) => { + if (planet.available) { + setSelectedPlanet(planet); + setIsBooked(false); + } + }; + + const handleBook = () => { + setIsBooked(true); + onSelect(selectedPlanet); + }; + + const handleBeginTrip = () => { + console.log('Beginning trip to', selectedPlanet.name); + }; + + if (isBooked) { + return ( + setIsBooked(false)} + /> + ); + } + + return ( +
+
+
+ Welcome, +

{user.name}

+
+ User avatar +
+ +
+
+

Let's book your

+

trip to

+
+ +
+ + +
+ +
+
+
p.id === selectedPlanet.id) + 1) / filteredPlanets.length) * 100}%` }} + /> +
+ +
+ {filteredPlanets.map((planet, index) => ( + handlePlanetSelect(planet)} + style={{ + backgroundColor: planet.id === selectedPlanet.id ? planet.color : (planet.available ? '#2C4F64' : '#4A4A4A') + }} + > +
+ {planet.name} +
+
+ ))} +
+
+ + + {selectedPlanet.name} +
+
+
+
+

{selectedPlanet.name}

+

{selectedPlanet.station}

+
+ +
+
+
+
+
+ ); +}; \ No newline at end of file diff --git a/components/(scenes)/travel/SolarSystem.tsx b/components/(scenes)/travel/SolarSystem.tsx index e23131a3..e73eb204 100644 --- a/components/(scenes)/travel/SolarSystem.tsx +++ b/components/(scenes)/travel/SolarSystem.tsx @@ -13,7 +13,7 @@ type Exoplanet = { name: string; distance: number; anomaly?: number; - planetType?: string; + planetType?: string; initialisationMissionId?: number; image?: string; description?: string; @@ -114,20 +114,13 @@ export default function SwitchPlanet() { const currentPlanets = planets[activeTab]; const currentPlanet = currentPlanets[currentIndex]; - const planetType = currentPlanet?.planetType || 'Unknown'; - const anomaly = currentPlanet?.anomaly ?? 'Unknown'; - const initialisationMissionId = currentPlanet?.initialisationMissionId ?? null; - const image = currentPlanet?.image ?? '/assets/Planets/DefaultExoplanet.png'; - const description = currentPlanet?.description ?? 'No description available'; + // const planetType = currentPlanet?.planetType || 'Unknown'; + // const anomaly = currentPlanet?.anomaly ?? 'Unknown'; + // const initialisationMissionId = currentPlanet?.initialisationMissionId ?? null; + // const image = currentPlanet?.image ?? '/assets/Planets/DefaultExoplanet.png'; + // const description = currentPlanet?.description ?? 'No description available'; const currentPlanetAnomaly = currentPlanet?.anomaly ?? 0; - useEffect(() => { - console.log("Component loaded, exoplanets: ", planets.exoplanets); - }, [planets.exoplanets]); - - // Fix for TS error 2345 - ensuring 'number | undefined' doesn't cause an issue - const index = currentPlanet?.anomaly !== undefined ? currentPlanet.anomaly : 0; - useEffect(() => { const fetchExoplanets = async () => { try { diff --git a/types/Travel.ts b/types/Travel.ts new file mode 100644 index 00000000..9f5b3a3d --- /dev/null +++ b/types/Travel.ts @@ -0,0 +1,36 @@ +export interface Planet { + id: string; + name: string; + type: 'intra-solar' | 'interstellar'; + image: string; + temperature: number; + station: string; + description: string; + color: string; + planetType: 'Unknown' | 'Terrestrial' | 'Gas Giant' | 'Ice Giant' | 'Dwarf Planet' | 'Lush' | string; + orbitPosition: number; + available: boolean; +}; + +export interface User { + id: string; + name: string; + avatar: string; + frequentFlyerNumber: string; + frequentFlyerStatus: 'Bronze' | 'Silver' | 'Gold' | 'Platinum'; +}; + +export interface Departure { + id: string; + rocketType: string; + departureTime: string; + price: number; + duration: string; + gate: string; +}; + +export interface BookingDetails { + user: User; + planet: Planet; + departure: Departure; +}; \ No newline at end of file