Skip to content

Commit ccffcd7

Browse files
committed
🪗〠 ↝ [SSC-45 SSC-51 SSG-59 SSG-85]: Improved tutorial/project introduction and relation context
1 parent 7dc69fb commit ccffcd7

File tree

7 files changed

+362
-47
lines changed

7 files changed

+362
-47
lines changed

‎app/page.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import { EarthViewLayout } from "@/components/(scenes)/planetScene/layout";
3636
import Onboarding from "./scenes/onboarding/page";
3737
import VerticalToolbar from "@/components/Layout/Toolbar";
3838
import StructureMissionGuide from "@/components/Layout/Guide";
39+
import SimpleeMissionGuide from "./tests/singleMissionGuide";
3940

4041
export default function Home() {
4142
const session = useSession();
@@ -94,7 +95,8 @@ export default function Home() {
9495
<AllAutomatonsOnActivePlanet />
9596
</center>
9697
</div>
97-
<div className="w-full py-2"><StructureMissionGuide />
98+
{/* <div className="w-full py-2"><StructureMissionGuide /> */}
99+
<div className="w-full py-2"><SimpleeMissionGuide />
98100
</div>
99101
</EarthViewLayout>
100102
// 60: <SaturnView />,

‎app/tests/missionsUnlocked.tsx

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { useEffect, useState } from "react";
2+
import { useSupabaseClient, useSession } from "@supabase/auth-helpers-react";
3+
4+
interface InventoryItem {
5+
id: number;
6+
configuration: {
7+
"missions unlocked"?: string[];
8+
[key: string]: any; // Allow other properties in configuration
9+
};
10+
}
11+
12+
const UserMissions = () => {
13+
const supabase = useSupabaseClient();
14+
const session = useSession();
15+
16+
const [missions, setMissions] = useState<string[]>([]);
17+
const [loading, setLoading] = useState(true);
18+
const [error, setError] = useState<string | null>(null);
19+
20+
useEffect(() => {
21+
if (!session?.user) return;
22+
23+
const fetchUserMissions = async () => {
24+
setLoading(true);
25+
setError(null);
26+
27+
try {
28+
// Query inventory for items owned by the user and containing missions unlocked
29+
const { data, error } = await supabase
30+
.from("inventory")
31+
.select("id, configuration")
32+
.eq("owner", session.user.id)
33+
.not("configuration->missions unlocked", "is", null); // Ensure "missions unlocked" exists
34+
35+
if (error) throw error;
36+
37+
// Extract and deduplicate all "missions unlocked"
38+
const unlockedMissions = data
39+
?.flatMap((item: InventoryItem) => item.configuration["missions unlocked"] || [])
40+
.filter((mission: string) => mission); // Filter out empty values
41+
42+
const uniqueMissions = Array.from(new Set(unlockedMissions));
43+
44+
setMissions(uniqueMissions);
45+
} catch (err) {
46+
console.error("Error fetching user missions:", err);
47+
setError("Failed to fetch missions. Please try again.");
48+
} finally {
49+
setLoading(false);
50+
}
51+
};
52+
53+
fetchUserMissions();
54+
}, [session?.user, supabase]);
55+
56+
if (!session?.user) return <p className="text-gray-400">Please log in to see your missions.</p>;
57+
58+
if (loading) return <p className="text-gray-400">Loading missions...</p>;
59+
60+
if (error) return <p className="text-red-500">{error}</p>;
61+
62+
if (missions.length === 0) {
63+
return <p className="text-gray-400">No missions unlocked yet.</p>;
64+
}
65+
66+
return (
67+
<div className="bg-gray-800 text-white rounded-lg p-6">
68+
<h1 className="text-xl font-bold mb-4">Missions Unlocked</h1>
69+
<ul className="list-disc pl-6">
70+
{missions.map((mission, index) => (
71+
<li key={index} className="mb-2">
72+
{mission}
73+
</li>
74+
))}
75+
</ul>
76+
</div>
77+
);
78+
};
79+
80+
export default UserMissions;

‎app/tests/page.tsx

+4-3
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,16 @@ import { MiningComponentComponent } from "@/components/(scenes)/mining/mining-co
66
import Greenhouse from "@/page.test";
77
import BigMap from "@/components/(scenes)/planetScene/bigMap";
88
import DiscoveriesPage from "@/content/Classifications/minimalDiscoveries";
9-
import PlanetHuntersSteps from "@/components/Structures/Missions/Astronomers/PlanetHunters/PlanetHunters";
9+
import UserMissions from "./missionsUnlocked";
10+
import SimpleeMissionGuide from "./singleMissionGuide";
1011
// import { TopographicMap } from "@/components/topographic-map";
1112

1213
export default function TestPage() {
1314
return (
1415
// <StarnetLayout>
1516
<>
16-
17-
<PlanetHuntersSteps />
17+
<UserMissions />
18+
<SimpleeMissionGuide />
1819
{/* <Greenhouse /> */}
1920
{/* <BigMap /> */}
2021
{/* <DiscoveriesPage /> */}

‎app/tests/singleMissionGuide.tsx

+240
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
import React, { useEffect, useState } from "react";
2+
import { Button } from "@/components/ui/button";
3+
import { Card, CardContent } from "@/components/ui/card";
4+
import { CloudDrizzleIcon, LightbulbIcon, Telescope, ArrowUpIcon, ArrowDownIcon } from "lucide-react";
5+
import { motion, AnimatePresence } from "framer-motion";
6+
import { useSupabaseClient, useSession } from "@supabase/auth-helpers-react";
7+
8+
// Define Mission and other types
9+
export interface Mission {
10+
id: number;
11+
name: string;
12+
description: string;
13+
additionalDescription: string; // New property for additional context
14+
icon: React.ElementType;
15+
color: string;
16+
identifier: string;
17+
}
18+
19+
const projects: Record<string, Mission[]> = {
20+
"Refracting Telescope": [
21+
{
22+
id: 1,
23+
name: "Planet Hunting",
24+
description: "Click on the Telescope structure icon to rate different transit events and find planets.",
25+
additionalDescription: "You'll work alongside other users to classify and verify planet candidates. Once they've been confirmed, you'll be able to visit them, determine their properties and start construction & exploration surveys.",
26+
icon: Telescope,
27+
color: "text-cyan-300",
28+
identifier: "planet"
29+
},
30+
{
31+
id: 2,
32+
name: "Asteroid Detection",
33+
description: "Click on the Telescope structure icon to discover new asteroids in your telescope's data.",
34+
additionalDescription: "Using the latest telescope data, you will identify potential asteroids that might pose a threat to the Earth or serve as future targets for exploration. Additionally, exo-asteroids can be discovered and tracked to identify their interactions with planets discovered in the Planet Hunters project",
35+
icon: Telescope,
36+
color: "text-green-300",
37+
identifier: "asteroid"
38+
},
39+
{
40+
id: 3,
41+
name: "Sunspot Observations",
42+
description: "Click on the Telescope structure icon to keep track of our sun's health & electrical storms",
43+
additionalDescription: "This mission requires monitoring sunspots and solar activity to understand how these phenomena affect space weather and our planet's magnetosphere. Sunspots can trigger electrical outages here on Earth (and on any planets in the firing line) - so we need to be prepared",
44+
icon: Telescope,
45+
color: "text-yellow-300",
46+
identifier: "sunspot"
47+
},
48+
],
49+
Biodome: [
50+
{
51+
id: 4,
52+
name: "Wildwatch Burrowing Owls",
53+
description: "Click on the Biodome structure icon to classify pictures of burrowing owls in captivity.",
54+
additionalDescription: "Help track the health and behavior of burrowing owls by classifying pictures from the Biodome. You will analyze the owls' nesting and foraging behaviors.",
55+
icon: LightbulbIcon,
56+
color: "text-cyan-300",
57+
identifier: "burrowingOwl"
58+
},
59+
{
60+
id: 5,
61+
name: "Iguanas from Above",
62+
description: "Count Galapagos Marine Iguanas from aerial photos that have been sent to your Biodome structure.",
63+
additionalDescription: "Use aerial images to count and classify marine iguanas on the Galapagos Islands. This is important for conservation efforts to track their population.",
64+
icon: LightbulbIcon,
65+
color: "text-green-300",
66+
identifier: "iguana"
67+
},
68+
],
69+
WeatherBalloon: [
70+
{
71+
id: 6,
72+
name: "Martian Cloud Survey",
73+
description: "Use your Weather Balloon to identify clouds & cloud behaviour on Mars & Mars-like planets",
74+
additionalDescription: "Analyze cloud patterns on Mars and other planets. This mission will involve classifying clouds based on their shape, size, and movement. We can use this information to understand more about the composition and climate cycle on these planets",
75+
icon: CloudDrizzleIcon,
76+
color: "text-blue-400",
77+
identifier: "martianClouds"
78+
},
79+
{
80+
id: 7,
81+
name: "Jovian Vortx Hunters",
82+
description: "Classify storms & storm behaviour on gas giant planets using data collected in your Weather Balloon",
83+
additionalDescription: "Study the massive storms on gas giants like Jupiter, tracking their movements and behaviors to understand their atmospheric dynamics, composition and climate.",
84+
icon: CloudDrizzleIcon,
85+
color: "text-yellow-400",
86+
identifier: "jovianStorms"
87+
},
88+
],
89+
};
90+
91+
const defaultMission: Mission = {
92+
id: 0,
93+
name: "Welcome to Star Sailors",
94+
description: "You've been given some basic structures to start your journey. Click on their icons to classify the data they've collected for you. New data & projects are being added weekly.",
95+
additionalDescription: "You'll get to explore various missions, starting with basic tasks such as classifying objects in the sky and on Earth. Each task you complete unlocks new insights. Next week (22nd December) you'll be able to upload your own discoveries and clips",
96+
icon: LightbulbIcon,
97+
color: "text-blue-400",
98+
identifier: "default-starting-mission",
99+
};
100+
101+
const structureTitles: Record<string, string> = {
102+
"Refracting Telescope": "Astronomer Missions",
103+
Biodome: "Ecology Missions",
104+
WeatherBalloon: "Meteorology Missions",
105+
};
106+
107+
const SimpleeMissionGuide = () => {
108+
const supabase = useSupabaseClient();
109+
const session = useSession();
110+
111+
const [completedIdentifiers, setCompletedIdentifiers] = useState<string[]>([]);
112+
const [activeStructure, setActiveStructure] = useState<string>("Refracting Telescope");
113+
const [isMinimized, setIsMinimized] = useState(false);
114+
115+
// Modal state
116+
const [isModalOpen, setIsModalOpen] = useState(false);
117+
const [selectedMission, setSelectedMission] = useState<Mission | null>(null);
118+
119+
useEffect(() => {
120+
const fetchClassifications = async () => {
121+
if (!session?.user) return;
122+
123+
try {
124+
const { data, error } = await supabase
125+
.from("classifications")
126+
.select("classificationtype, author")
127+
.eq("author", session.user.id);
128+
129+
if (error) throw error;
130+
131+
const identifiers = data.map((item: any) => item.classificationtype).filter(Boolean);
132+
setCompletedIdentifiers(identifiers);
133+
} catch (err) {
134+
console.error("Error fetching classifications:", err);
135+
}
136+
};
137+
138+
fetchClassifications();
139+
}, [session?.user, supabase]);
140+
141+
const allStructures = Object.keys(projects);
142+
const currentMissions = projects[activeStructure] || [];
143+
144+
const handlePrevStructure = () => {
145+
const currentIndex = allStructures.indexOf(activeStructure);
146+
const prevIndex = (currentIndex - 1 + allStructures.length) % allStructures.length;
147+
setActiveStructure(allStructures[prevIndex]);
148+
};
149+
150+
const handleNextStructure = () => {
151+
const currentIndex = allStructures.indexOf(activeStructure);
152+
const nextIndex = (currentIndex + 1) % allStructures.length;
153+
setActiveStructure(allStructures[nextIndex]);
154+
};
155+
156+
const renderMission = (mission: Mission) => {
157+
const isCompleted = completedIdentifiers.includes(mission.identifier);
158+
159+
return (
160+
<Card key={mission.id} className={`cursor-pointer border mb-2 ${isCompleted ? "line-through" : ""} bg-gray-800`}>
161+
<CardContent className="p-4 flex items-center space-x-4">
162+
<mission.icon className={`w-8 h-8 ${mission.color}`} />
163+
<div>
164+
<h3 className="text-lg font-semibold text-gray-200">{mission.name}</h3>
165+
<p className="text-sm text-gray-400">{mission.description}</p>
166+
<Button
167+
className="mt-2"
168+
variant="outline" // Outline button style
169+
onClick={() => {
170+
setSelectedMission(mission);
171+
setIsModalOpen(true);
172+
}}
173+
>
174+
Read More
175+
</Button>
176+
</div>
177+
</CardContent>
178+
</Card>
179+
);
180+
};
181+
182+
const closeModal = () => {
183+
setIsModalOpen(false);
184+
setSelectedMission(null);
185+
};
186+
187+
return (
188+
<motion.div
189+
className="p-4 max-w-6xl mx-auto font-mono"
190+
initial={{ height: isMinimized ? "50px" : "auto" }}
191+
animate={{ height: isMinimized ? "50px" : "auto" }}
192+
transition={{ duration: 0.5 }}
193+
>
194+
<div className="flex justify-between items-center mb-4">
195+
{!isMinimized && <Button onClick={handlePrevStructure}>&larr; Previous</Button>}
196+
<div className="flex items-center space-x-2">
197+
<h2 className="text-xl font-semibold text-gray-200">{structureTitles[activeStructure]}</h2>
198+
<Button
199+
variant="destructive"
200+
onClick={() => setIsMinimized(!isMinimized)}
201+
className="flex items-center justify-center w-14 h-8"
202+
>
203+
{isMinimized && <p>Open</p>}
204+
{!isMinimized && <p>Close</p>}
205+
</Button>
206+
</div>
207+
{!isMinimized && <Button onClick={handleNextStructure}>Next &rarr;</Button>}
208+
</div>
209+
210+
<AnimatePresence>
211+
{!isMinimized && (
212+
<motion.div
213+
className="h-[calc(2*96px)] overflow-y-scroll space-y-2"
214+
initial={{ opacity: 0, y: -10 }}
215+
animate={{ opacity: 1, y: 0 }}
216+
exit={{ opacity: 0, y: -10 }}
217+
transition={{ duration: 0.3 }}
218+
>
219+
{renderMission(defaultMission)}
220+
{currentMissions.map((mission) => renderMission(mission))}
221+
</motion.div>
222+
)}
223+
</AnimatePresence>
224+
225+
{isModalOpen && selectedMission && (
226+
<div className="fixed inset-0 flex justify-center items-center bg-black bg-opacity-50">
227+
<div className="bg-[#2C4F64] p-4 max-w-md w-full rounded-lg">
228+
<h3 className="text-xl font-semibold mb-2">{selectedMission.name}</h3>
229+
<p>{selectedMission.additionalDescription}</p> {/* Display additional description */}
230+
<Button onClick={closeModal} className="mt-4">
231+
Close
232+
</Button>
233+
</div>
234+
</div>
235+
)}
236+
</motion.div>
237+
);
238+
};
239+
240+
export default SimpleeMissionGuide;

‎components/Missions/mission-selector.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ const structures: Structure[] = [
5353
{
5454
name: 'Refracting Telescope',
5555
icon: Rocket,
56-
description: 'Browse & classify space-based observations & classifications',
56+
description: 'Browse & classify space-based observations & classifications',
5757
bgColor: 'bg-indigo-900',
5858
accentColor: 'text-purple-400',
5959
shape: <div className="absolute top-0 right-0 w-32 h-32 bg-purple-800 rounded-full -mr-16 -mt-16 opacity-20"></div>,

0 commit comments

Comments
 (0)