diff --git a/components/Structures/Missions/Astronomers/DailyMinorPlanet/DMPVote.tsx b/components/Structures/Missions/Astronomers/DailyMinorPlanet/DMPVote.tsx
index ecbb40b2..190b692f 100644
--- a/components/Structures/Missions/Astronomers/DailyMinorPlanet/DMPVote.tsx
+++ b/components/Structures/Missions/Astronomers/DailyMinorPlanet/DMPVote.tsx
@@ -28,7 +28,7 @@ export default function VoteDMPClassifications() {
setError("User session not found.");
setLoading(false);
return;
- }
+ };
setLoading(true);
setError(null);
@@ -45,15 +45,12 @@ export default function VoteDMPClassifications() {
const media = classification.media;
let images: string[] = [];
- // Check if media is an array with at least 2 elements (array structure like [[], [...image URLs]])
if (Array.isArray(media) && media.length > 1) {
- // Extract the image URLs from the second element if it's an array of URLs
images = media[1] && Array.isArray(media[1]) ? media[1] : [];
}
- // Check if there's a single image URL (media is not an array but an object with uploadUrl)
else if (media && typeof media === "object" && media.uploadUrl) {
images.push(media.uploadUrl);
- }
+ }''
const votes = classification.classificationConfiguration?.votes || 0;
@@ -66,7 +63,7 @@ export default function VoteDMPClassifications() {
setError("Failed to load classifications.");
} finally {
setLoading(false);
- }
+ }''
};
useEffect(() => {
@@ -100,7 +97,7 @@ export default function VoteDMPClassifications() {
}
} catch (error) {
console.error("Error voting:", error);
- }
+ };
};
return (
diff --git a/components/Structures/Missions/Astronomers/DailyMinorPlanet/DailyMinorPlanet.tsx b/components/Structures/Missions/Astronomers/DailyMinorPlanet/DailyMinorPlanet.tsx
index 76f0aae4..fec27487 100644
--- a/components/Structures/Missions/Astronomers/DailyMinorPlanet/DailyMinorPlanet.tsx
+++ b/components/Structures/Missions/Astronomers/DailyMinorPlanet/DailyMinorPlanet.tsx
@@ -120,8 +120,8 @@ const DailyMinorPlanetMissions = () => {
const [currentChapter, setCurrentChapter] = useState(1);
const maxUnlockedChapter = Math.max(
- Math.floor(experiencePoints / 9) + 1, // Based on experience points
- Math.max(...missions.map(mission => mission.chapter)) // Ensure higher chapters are unlocked if there are missions in them
+ Math.floor(experiencePoints / 9) + 1,
+ Math.max(...missions.map(mission => mission.chapter))
);
useEffect(() => {
diff --git a/components/Structures/Missions/Astronomers/PlanetHunters/PHVote.tsx b/components/Structures/Missions/Astronomers/PlanetHunters/PHVote.tsx
index 56331072..3673c235 100644
--- a/components/Structures/Missions/Astronomers/PlanetHunters/PHVote.tsx
+++ b/components/Structures/Missions/Astronomers/PlanetHunters/PHVote.tsx
@@ -28,7 +28,7 @@ export default function VotePlanetClassifictions() {
if (!session?.user) {
setError("User session not found.");
setLoading(false);
- return;
+ return;
};
setLoading(true);
diff --git a/components/Structures/Missions/Meteorologists/Cloudspotting/CloudspottingOnMars.tsx b/components/Structures/Missions/Meteorologists/Cloudspotting/CloudspottingOnMars.tsx
new file mode 100644
index 00000000..f5062ff8
--- /dev/null
+++ b/components/Structures/Missions/Meteorologists/Cloudspotting/CloudspottingOnMars.tsx
@@ -0,0 +1,194 @@
+import { useEffect, useState } from "react";
+import { useSupabaseClient, useSession } from "@supabase/auth-helpers-react";
+import MissionShell from "../../BasePlate";
+import { CloudCogIcon, FolderCog, Vote } from "lucide-react";
+import { StarterLidar } from "@/components/Projects/Lidar/Clouds";
+import VoteCoMClassifications from "./CoMVote";
+
+const CloudspottingOnMars = () => {
+ const supabase = useSupabaseClient();
+ const session = useSession();
+
+ const [missions, setMissions] = useState([
+ {
+ id: 1,
+ chapter: 1,
+ title: "Make a cloud classification",
+ description:
+ "Use your weather balloon & LIDAR technologies to discover properties about Martian clouds",
+ icon: CloudCogIcon,
+ points: 2,
+ completedCount: 0,
+ internalComponent: () => ,
+ color: 'text-blue-500',
+ shadow: false,
+ action: () => {},
+ },
+ {
+ id: 2,
+ chapter: 1,
+ title: "Propose a cloud in your classifications",
+ description: "Make a classification indicating a positive cloud candidate",
+ icon: FolderCog,
+ points: 1,
+ completedCount: 0,
+ internalComponent: () => ,
+ color: 'text-cyan-300',
+ shadow: false,
+ action: () => {},
+ },
+ {
+ id: 3,
+ chapter: 1,
+ title: "Comment or vote on a cloud classification",
+ description:
+ "Collaborate with other players to rate proposed cloud candidates and behaviour",
+ icon: Vote,
+ points: 1,
+ completedCount: 0,
+ internalComponent: () => ,
+ color: 'text-green-700',
+ shadow: false,
+ action: () => []
+ },
+ {
+ id: 4,
+ chapter: 2,
+ title: "Have a cloud candidate confirmed",
+ description:
+ "Validate the presence of a cloud candidate by confirming it through analysis and collaboration.",
+ icon: CloudCogIcon,
+ points: 3,
+ completedCount: 0,
+ internalComponent: () =>
,
+ color: 'text-red-500',
+ shadow: false,
+ action: () => {}
+ },
+ {
+ id: 5,
+ chapter: 2,
+ title: "Connect to a planet (terrestrial list)",
+ description:
+ "Establish a connection to a planet within the terrestrial list and begin analyzing cloud patterns.",
+ icon: FolderCog,
+ points: 2,
+ completedCount: 0,
+ internalComponent: () => ,
+ color: 'text-yellow-500',
+ shadow: false,
+ action: () => {}
+ },
+ {
+ id: 6,
+ chapter: 2,
+ title: "Propose temperature range",
+ description:
+ "Submit your proposed temperature range for cloud formation based on your data and observations.",
+ icon: CloudCogIcon,
+ points: 2,
+ completedCount: 0,
+ internalComponent: () => ,
+ color: 'text-blue-300',
+ shadow: false,
+ action: () => {}
+ },
+ {
+ id: 7,
+ chapter: 2,
+ title: "Make a CoM shapes classification",
+ description:
+ "Classify cloud formations based on their Center of Mass (CoM) shapes to identify patterns.",
+ icon: CloudCogIcon,
+ points: 2,
+ completedCount: 0,
+ internalComponent: () => ,
+ color: 'text-green-300',
+ shadow: false,
+ action: () => {}
+ },
+ {
+ id: 8,
+ chapter: 2,
+ title: "Comment/vote on a shapes classification",
+ description:
+ "Engage with other players by commenting on or voting for cloud shapes classifications to improve the system.",
+ icon: Vote,
+ points: 1,
+ completedCount: 0,
+ internalComponent: () => ,
+ color: 'text-pink-500',
+ shadow: false,
+ action: () => {}
+ },
+ {
+ id: 9,
+ chapter: 2,
+ title: "Propose cloud type (1 of the 6 major ones)",
+ description:
+ "Propose a cloud type (from the 6 major types) and include the corresponding temperature range, outputting a shape classification.",
+ icon: FolderCog,
+ points: 3,
+ completedCount: 0,
+ internalComponent: () => ,
+ color: 'text-orange-500',
+ shadow: false,
+ action: () => {}
+ },
+ {
+ id: 10,
+ chapter: 2,
+ title: "Connect a cloud shape with type to a regular cloud",
+ description:
+ "Link a cloud shape classification to a regular cloud type, verifying its characteristics.",
+ icon: CloudCogIcon,
+ points: 2,
+ completedCount: 0,
+ internalComponent: () => ,
+ color: 'text-purple-500',
+ shadow: false,
+ action: () => {}
+ }
+ ]);
+
+ const [experiencePoints, setExperiencePoints] = useState(0);
+ const [level, setLevel] = useState(1);
+ const [currentChapter, setCurrentChapter] = useState(1);
+
+ const maxUnlockedChapter = Math.max(
+ Math.floor(experiencePoints / 9) + 1,
+ Math.max(...missions.map(mission => mission.chapter))
+ );
+
+ useEffect(() => {
+ if (!session) {
+ return;
+ };
+
+ const fetchMissionData = async () => {
+ // Fetch and update mission data from Supabase if needed
+ }
+ }, [supabase, session]);
+
+ const handlePreviousChapter = () => {
+ if (currentChapter > 1) setCurrentChapter(currentChapter - 1);
+ };
+
+ const handleNextChapter = () => {
+ if (currentChapter < maxUnlockedChapter) setCurrentChapter(currentChapter + 1);
+ };
+
+ return (
+ mission.chapter === currentChapter)}
+ experiencePoints={experiencePoints}
+ level={level}
+ currentChapter={currentChapter}
+ maxUnlockedChapter={maxUnlockedChapter}
+ onPreviousChapter={handlePreviousChapter}
+ onNextChapter={handleNextChapter}
+ />
+ );
+};
+
+export default CloudspottingOnMars;
\ No newline at end of file
diff --git a/components/Structures/Missions/Meteorologists/Cloudspotting/CoMVote.tsx b/components/Structures/Missions/Meteorologists/Cloudspotting/CoMVote.tsx
new file mode 100644
index 00000000..5c07d93f
--- /dev/null
+++ b/components/Structures/Missions/Meteorologists/Cloudspotting/CoMVote.tsx
@@ -0,0 +1,130 @@
+'use client';
+
+import React, { useEffect, useState } from "react";
+import { PostCardSingle } from "@/content/Posts/PostSingle";
+import { useSession, useSupabaseClient } from "@supabase/auth-helpers-react";
+import StarnetLayout from "@/components/Layout/Starnet";
+
+interface Classification {
+ id: number;
+ created_at: string;
+ content: string | null;
+ author: string | null;
+ anomaly: number | null;
+ media: any | null;
+ classificationtype: string | null;
+ classificationConfiguration: any | null;
+};
+
+export default function VotePlanetClassifictions() {
+ const supabase = useSupabaseClient();
+ const session = useSession();
+
+ const [classifications, setClassifications] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+
+ const fetchClassifications = async () => {
+ if (!session?.user) {
+ setError("User session not found.");
+ setLoading(false);
+ return;
+ };
+
+ setLoading(true);
+ setError(null);
+ try {
+ const { data, error } = await supabase
+ .from('classifications')
+ .select('*')
+ .eq('classificationtype', 'cloud')
+ .order('created_at', { ascending: false }) as { data: Classification[]; error: any };
+
+ if (error) throw error;
+
+ const processedData = data.map((classification) => {
+ const media = classification.media;
+ let images: string[] = [];
+
+ if (Array.isArray(media) && media.length === 2 && typeof media[1] === "string") {
+ images.push(media[1]);
+ } else if (media && media.uploadUrl) {
+ images.push(media.uploadUrl);
+ }
+
+ const votes = classification.classificationConfiguration?.votes || 0;
+
+ return { ...classification, images, votes };
+ });
+
+ setClassifications(processedData);
+ } catch (error) {
+ console.error("Error fetching classifications:", error);
+ setError("Failed to load classifications.");
+ } finally {
+ setLoading(false);
+ };
+ };
+
+ useEffect(() => {
+ fetchClassifications();
+ }, [session])
+
+ const handleVote = async (classificationId: number, currentConfig: any) => {
+ try {
+ const currentVotes = currentConfig?.votes || 0;
+
+ const updatedConfig = {
+ ...currentConfig,
+ votes: currentVotes + 1,
+ };
+
+ const { error } = await supabase
+ .from("classifications")
+ .update({ classificationConfiguration: updatedConfig })
+ .eq("id", classificationId);
+
+ if (error) {
+ console.error("Error updating classificationConfiguration:", error);
+ } else {
+ setClassifications((prevClassifications) =>
+ prevClassifications.map((classification) =>
+ classification.id === classificationId
+ ? { ...classification, votes: updatedConfig.votes }
+ : classification
+ )
+ );
+ }
+ } catch (error) {
+ console.error("Error voting:", error);
+ };
+ };
+
+ return (
+
+ {loading ? (
+
Loading classifications...
+ ) : error ? (
+
{error}
+ ) : (
+ classifications.map((classification) => (
+
handleVote(classification.id, classification.classificationConfiguration)}
+ />
+ ))
+ )}
+
+ );
+};
\ No newline at end of file
diff --git a/constants/Structures/Properties.tsx b/constants/Structures/Properties.tsx
index 96fa7481..87846777 100644
--- a/constants/Structures/Properties.tsx
+++ b/constants/Structures/Properties.tsx
@@ -30,6 +30,7 @@ import { ZoodexIguanas } from "@/components/Projects/Zoodex/iguanasFromAbove";
import PlanetHuntersSteps from "@/components/Structures/Missions/Astronomers/PlanetHunters/PlanetHunters";
import { useRouter } from 'next/router';
import DailyMinorPlanetMissions from "@/components/Structures/Missions/Astronomers/DailyMinorPlanet/DailyMinorPlanet";
+import CloudspottingOnMars from "@/components/Structures/Missions/Meteorologists/Cloudspotting/CloudspottingOnMars";
interface IndividualStructureProps {
name?: string;
@@ -285,7 +286,7 @@ export const StructuresConfig: StructureConfig = {
{
icon: ,
text: "Search your clouds",
- dynamicComponent: ,
+ dynamicComponent: ,
sizePercentage: 60,
},
{
diff --git a/content/Posts/PostSingle.tsx b/content/Posts/PostSingle.tsx
index d2fe1629..b7cb229d 100644
--- a/content/Posts/PostSingle.tsx
+++ b/content/Posts/PostSingle.tsx
@@ -29,7 +29,7 @@ interface PostCardSingleProps {
votes: number;
category: string;
tags?: string[];
- classificationConfig?: any;
+ classificationConfig?: any;
images: string[];
classificationType: string;
onVote?: () => void;