Skip to content

Commit

Permalink
🪗〠 ↝ [SSC-45 SSC-51 SSG-59 SSG-85]: Improved tutorial/project introdu…
Browse files Browse the repository at this point in the history
…ction and relation context
  • Loading branch information
Gizmotronn committed Dec 16, 2024
1 parent 7dc69fb commit ccffcd7
Show file tree
Hide file tree
Showing 7 changed files with 362 additions and 47 deletions.
4 changes: 3 additions & 1 deletion app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { EarthViewLayout } from "@/components/(scenes)/planetScene/layout";
import Onboarding from "./scenes/onboarding/page";
import VerticalToolbar from "@/components/Layout/Toolbar";
import StructureMissionGuide from "@/components/Layout/Guide";
import SimpleeMissionGuide from "./tests/singleMissionGuide";

export default function Home() {
const session = useSession();
Expand Down Expand Up @@ -94,7 +95,8 @@ export default function Home() {
<AllAutomatonsOnActivePlanet />
</center>
</div>
<div className="w-full py-2"><StructureMissionGuide />
{/* <div className="w-full py-2"><StructureMissionGuide /> */}
<div className="w-full py-2"><SimpleeMissionGuide />
</div>
</EarthViewLayout>
// 60: <SaturnView />,
Expand Down
80 changes: 80 additions & 0 deletions app/tests/missionsUnlocked.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { useEffect, useState } from "react";
import { useSupabaseClient, useSession } from "@supabase/auth-helpers-react";

interface InventoryItem {
id: number;
configuration: {
"missions unlocked"?: string[];
[key: string]: any; // Allow other properties in configuration
};
}

const UserMissions = () => {
const supabase = useSupabaseClient();
const session = useSession();

const [missions, setMissions] = useState<string[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);

useEffect(() => {
if (!session?.user) return;

const fetchUserMissions = async () => {
setLoading(true);
setError(null);

try {
// Query inventory for items owned by the user and containing missions unlocked
const { data, error } = await supabase
.from("inventory")
.select("id, configuration")
.eq("owner", session.user.id)
.not("configuration->missions unlocked", "is", null); // Ensure "missions unlocked" exists

if (error) throw error;

// Extract and deduplicate all "missions unlocked"
const unlockedMissions = data
?.flatMap((item: InventoryItem) => item.configuration["missions unlocked"] || [])
.filter((mission: string) => mission); // Filter out empty values

const uniqueMissions = Array.from(new Set(unlockedMissions));

setMissions(uniqueMissions);
} catch (err) {
console.error("Error fetching user missions:", err);
setError("Failed to fetch missions. Please try again.");
} finally {
setLoading(false);
}
};

fetchUserMissions();
}, [session?.user, supabase]);

if (!session?.user) return <p className="text-gray-400">Please log in to see your missions.</p>;

if (loading) return <p className="text-gray-400">Loading missions...</p>;

if (error) return <p className="text-red-500">{error}</p>;

if (missions.length === 0) {
return <p className="text-gray-400">No missions unlocked yet.</p>;
}

return (
<div className="bg-gray-800 text-white rounded-lg p-6">
<h1 className="text-xl font-bold mb-4">Missions Unlocked</h1>
<ul className="list-disc pl-6">
{missions.map((mission, index) => (
<li key={index} className="mb-2">
{mission}
</li>
))}
</ul>
</div>
);
};

export default UserMissions;
7 changes: 4 additions & 3 deletions app/tests/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ import { MiningComponentComponent } from "@/components/(scenes)/mining/mining-co
import Greenhouse from "@/page.test";
import BigMap from "@/components/(scenes)/planetScene/bigMap";
import DiscoveriesPage from "@/content/Classifications/minimalDiscoveries";
import PlanetHuntersSteps from "@/components/Structures/Missions/Astronomers/PlanetHunters/PlanetHunters";
import UserMissions from "./missionsUnlocked";
import SimpleeMissionGuide from "./singleMissionGuide";
// import { TopographicMap } from "@/components/topographic-map";

export default function TestPage() {
return (
// <StarnetLayout>
<>

<PlanetHuntersSteps />
<UserMissions />
<SimpleeMissionGuide />
{/* <Greenhouse /> */}
{/* <BigMap /> */}
{/* <DiscoveriesPage /> */}
Expand Down
240 changes: 240 additions & 0 deletions app/tests/singleMissionGuide.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
import React, { useEffect, useState } from "react";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { CloudDrizzleIcon, LightbulbIcon, Telescope, ArrowUpIcon, ArrowDownIcon } from "lucide-react";
import { motion, AnimatePresence } from "framer-motion";
import { useSupabaseClient, useSession } from "@supabase/auth-helpers-react";

// Define Mission and other types
export interface Mission {
id: number;
name: string;
description: string;
additionalDescription: string; // New property for additional context
icon: React.ElementType;
color: string;
identifier: string;
}

const projects: Record<string, Mission[]> = {
"Refracting Telescope": [
{
id: 1,
name: "Planet Hunting",
description: "Click on the Telescope structure icon to rate different transit events and find planets.",
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.",
icon: Telescope,
color: "text-cyan-300",
identifier: "planet"
},
{
id: 2,
name: "Asteroid Detection",
description: "Click on the Telescope structure icon to discover new asteroids in your telescope's data.",
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",
icon: Telescope,
color: "text-green-300",
identifier: "asteroid"
},
{
id: 3,
name: "Sunspot Observations",
description: "Click on the Telescope structure icon to keep track of our sun's health & electrical storms",
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",
icon: Telescope,
color: "text-yellow-300",
identifier: "sunspot"
},
],
Biodome: [
{
id: 4,
name: "Wildwatch Burrowing Owls",
description: "Click on the Biodome structure icon to classify pictures of burrowing owls in captivity.",
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.",
icon: LightbulbIcon,
color: "text-cyan-300",
identifier: "burrowingOwl"
},
{
id: 5,
name: "Iguanas from Above",
description: "Count Galapagos Marine Iguanas from aerial photos that have been sent to your Biodome structure.",
additionalDescription: "Use aerial images to count and classify marine iguanas on the Galapagos Islands. This is important for conservation efforts to track their population.",
icon: LightbulbIcon,
color: "text-green-300",
identifier: "iguana"
},
],
WeatherBalloon: [
{
id: 6,
name: "Martian Cloud Survey",
description: "Use your Weather Balloon to identify clouds & cloud behaviour on Mars & Mars-like planets",
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",
icon: CloudDrizzleIcon,
color: "text-blue-400",
identifier: "martianClouds"
},
{
id: 7,
name: "Jovian Vortx Hunters",
description: "Classify storms & storm behaviour on gas giant planets using data collected in your Weather Balloon",
additionalDescription: "Study the massive storms on gas giants like Jupiter, tracking their movements and behaviors to understand their atmospheric dynamics, composition and climate.",
icon: CloudDrizzleIcon,
color: "text-yellow-400",
identifier: "jovianStorms"
},
],
};

const defaultMission: Mission = {
id: 0,
name: "Welcome to Star Sailors",
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.",
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",
icon: LightbulbIcon,
color: "text-blue-400",
identifier: "default-starting-mission",
};

const structureTitles: Record<string, string> = {
"Refracting Telescope": "Astronomer Missions",
Biodome: "Ecology Missions",
WeatherBalloon: "Meteorology Missions",
};

const SimpleeMissionGuide = () => {
const supabase = useSupabaseClient();
const session = useSession();

const [completedIdentifiers, setCompletedIdentifiers] = useState<string[]>([]);
const [activeStructure, setActiveStructure] = useState<string>("Refracting Telescope");
const [isMinimized, setIsMinimized] = useState(false);

// Modal state
const [isModalOpen, setIsModalOpen] = useState(false);
const [selectedMission, setSelectedMission] = useState<Mission | null>(null);

useEffect(() => {
const fetchClassifications = async () => {
if (!session?.user) return;

try {
const { data, error } = await supabase
.from("classifications")
.select("classificationtype, author")
.eq("author", session.user.id);

if (error) throw error;

const identifiers = data.map((item: any) => item.classificationtype).filter(Boolean);
setCompletedIdentifiers(identifiers);
} catch (err) {
console.error("Error fetching classifications:", err);
}
};

fetchClassifications();
}, [session?.user, supabase]);

const allStructures = Object.keys(projects);
const currentMissions = projects[activeStructure] || [];

const handlePrevStructure = () => {
const currentIndex = allStructures.indexOf(activeStructure);
const prevIndex = (currentIndex - 1 + allStructures.length) % allStructures.length;
setActiveStructure(allStructures[prevIndex]);
};

const handleNextStructure = () => {
const currentIndex = allStructures.indexOf(activeStructure);
const nextIndex = (currentIndex + 1) % allStructures.length;
setActiveStructure(allStructures[nextIndex]);
};

const renderMission = (mission: Mission) => {
const isCompleted = completedIdentifiers.includes(mission.identifier);

return (
<Card key={mission.id} className={`cursor-pointer border mb-2 ${isCompleted ? "line-through" : ""} bg-gray-800`}>
<CardContent className="p-4 flex items-center space-x-4">
<mission.icon className={`w-8 h-8 ${mission.color}`} />
<div>
<h3 className="text-lg font-semibold text-gray-200">{mission.name}</h3>
<p className="text-sm text-gray-400">{mission.description}</p>
<Button
className="mt-2"
variant="outline" // Outline button style
onClick={() => {
setSelectedMission(mission);
setIsModalOpen(true);
}}
>
Read More
</Button>
</div>
</CardContent>
</Card>
);
};

const closeModal = () => {
setIsModalOpen(false);
setSelectedMission(null);
};

return (
<motion.div
className="p-4 max-w-6xl mx-auto font-mono"
initial={{ height: isMinimized ? "50px" : "auto" }}
animate={{ height: isMinimized ? "50px" : "auto" }}
transition={{ duration: 0.5 }}
>
<div className="flex justify-between items-center mb-4">
{!isMinimized && <Button onClick={handlePrevStructure}>&larr; Previous</Button>}
<div className="flex items-center space-x-2">
<h2 className="text-xl font-semibold text-gray-200">{structureTitles[activeStructure]}</h2>
<Button
variant="destructive"
onClick={() => setIsMinimized(!isMinimized)}
className="flex items-center justify-center w-14 h-8"
>
{isMinimized && <p>Open</p>}
{!isMinimized && <p>Close</p>}
</Button>
</div>
{!isMinimized && <Button onClick={handleNextStructure}>Next &rarr;</Button>}
</div>

<AnimatePresence>
{!isMinimized && (
<motion.div
className="h-[calc(2*96px)] overflow-y-scroll space-y-2"
initial={{ opacity: 0, y: -10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
transition={{ duration: 0.3 }}
>
{renderMission(defaultMission)}
{currentMissions.map((mission) => renderMission(mission))}
</motion.div>
)}
</AnimatePresence>

{isModalOpen && selectedMission && (
<div className="fixed inset-0 flex justify-center items-center bg-black bg-opacity-50">
<div className="bg-[#2C4F64] p-4 max-w-md w-full rounded-lg">
<h3 className="text-xl font-semibold mb-2">{selectedMission.name}</h3>
<p>{selectedMission.additionalDescription}</p> {/* Display additional description */}
<Button onClick={closeModal} className="mt-4">
Close
</Button>
</div>
</div>
)}
</motion.div>
);
};

export default SimpleeMissionGuide;
2 changes: 1 addition & 1 deletion components/Missions/mission-selector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const structures: Structure[] = [
{
name: 'Refracting Telescope',
icon: Rocket,
description: 'Browse & classify space-based observations & classifications',
description: 'Browse & classify space-based observations & classifications',
bgColor: 'bg-indigo-900',
accentColor: 'text-purple-400',
shape: <div className="absolute top-0 right-0 w-32 h-32 bg-purple-800 rounded-full -mr-16 -mt-16 opacity-20"></div>,
Expand Down
Loading

0 comments on commit ccffcd7

Please sign in to comment.