From 456666ea3469b3e5ef0a1c13de38abc8a9aea675 Mon Sep 17 00:00:00 2001 From: Gizmotronn Date: Thu, 26 Dec 2024 11:19:04 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=B5=F0=9F=8F=BB=E2=80=8D=E2=99=80?= =?UTF-8?q?=EF=B8=8F=F0=9F=8C=88=20=E2=86=9D=20[SSP-39=20SSP-42]:=20Genera?= =?UTF-8?q?tors=20can=20now=20show=20in=20missions=20inside=20alternate=20?= =?UTF-8?q?post=20card=20views?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PlanetHunters/PlanetHunters.tsx | 11 +- .../Astronomers/PlanetHunters/PlanetMaker.tsx | 99 ++++++++++++ .../Cloudspotting/CloudMaker.tsx | 99 ++++++++++++ .../Cloudspotting/CloudSignal.tsx | 2 +- .../Cloudspotting/CloudspottingOnMars.tsx | 19 ++- .../Meteorologists/Cloudspotting/CoMVote.tsx | 2 +- content/Posts/PostWithGen.tsx | 150 ++++++++++++++++++ 7 files changed, 373 insertions(+), 9 deletions(-) create mode 100644 components/Structures/Missions/Astronomers/PlanetHunters/PlanetMaker.tsx create mode 100644 components/Structures/Missions/Meteorologists/Cloudspotting/CloudMaker.tsx create mode 100644 content/Posts/PostWithGen.tsx diff --git a/components/Structures/Missions/Astronomers/PlanetHunters/PlanetHunters.tsx b/components/Structures/Missions/Astronomers/PlanetHunters/PlanetHunters.tsx index d08ade42..e590d327 100644 --- a/components/Structures/Missions/Astronomers/PlanetHunters/PlanetHunters.tsx +++ b/components/Structures/Missions/Astronomers/PlanetHunters/PlanetHunters.tsx @@ -1,9 +1,10 @@ import { useEffect, useState } from "react"; import { useSupabaseClient, useSession } from "@supabase/auth-helpers-react"; -import { TelescopeIcon, RadioIcon, SpeakerIcon, DiscAlbum, PersonStandingIcon } from "lucide-react"; +import { TelescopeIcon, RadioIcon, SpeakerIcon, DiscAlbum, PersonStandingIcon, Paintbrush2 } from "lucide-react"; import PlanetTypeCommentForm from "./PlanetType"; import { StarterTelescopeTess } from "@/components/Projects/Telescopes/Transiting"; import VotePlanetClassifictions from "./PHVote"; +import PHClassificationGenerator from "./PlanetMaker"; interface MissionStep { id: number; @@ -155,9 +156,9 @@ const PlanetHuntersSteps = () => { }, { id: 5, - title: "Demo Mission for Chapter 3", - description: "This is a demo mission to simulate progress in chapter 3.", - icon: PersonStandingIcon, + title: "Make your own planet design", + description: "You're now able to start creating visual representations of your discoveries. These will become more advanced and accurate the more data you discover", + icon: Paintbrush2, action: () => {}, completedCount: 0, color: "text-yellow-500", @@ -193,7 +194,7 @@ const PlanetHuntersSteps = () => { {selectedMission.id === 2 && } {selectedMission.id === 3 && } {selectedMission.id === 4 && } - {selectedMission.id === 5 &&
Demo Mission for Chapter 3
} + {selectedMission.id === 5 && } diff --git a/components/Structures/Missions/Astronomers/PlanetHunters/PlanetMaker.tsx b/components/Structures/Missions/Astronomers/PlanetHunters/PlanetMaker.tsx new file mode 100644 index 00000000..b83355b9 --- /dev/null +++ b/components/Structures/Missions/Astronomers/PlanetHunters/PlanetMaker.tsx @@ -0,0 +1,99 @@ +"use client"; + +import React, { useEffect, useState } from "react"; +import { PostCardSingleWithGenerator } from "@/content/Posts/PostWithGen"; +import { useSession, useSupabaseClient } from "@supabase/auth-helpers-react"; + +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 PHClassificationGenerator() { + 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("author", session.user.id) + .eq('classificationtype', 'planet') + .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]); + + return ( +
+ {loading ? ( +

Loading classifications

+ ) : error ? ( +

{error}

+ ) : ( + classifications.map((classification) => ( + + )) + )} +
+ ); +}; \ No newline at end of file diff --git a/components/Structures/Missions/Meteorologists/Cloudspotting/CloudMaker.tsx b/components/Structures/Missions/Meteorologists/Cloudspotting/CloudMaker.tsx new file mode 100644 index 00000000..34295853 --- /dev/null +++ b/components/Structures/Missions/Meteorologists/Cloudspotting/CloudMaker.tsx @@ -0,0 +1,99 @@ +"use client"; + +import React, { useEffect, useState } from "react"; +import { PostCardSingleWithGenerator } from "@/content/Posts/PostWithGen"; +import { useSession, useSupabaseClient } from "@supabase/auth-helpers-react"; + +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 CloudClassificationGenerator() { + 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("author", session.user.id) + .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]); + + return ( +
+ {loading ? ( +

Loading classifications

+ ) : error ? ( +

{error}

+ ) : ( + classifications.map((classification) => ( + + )) + )} +
+ ); +}; \ No newline at end of file diff --git a/components/Structures/Missions/Meteorologists/Cloudspotting/CloudSignal.tsx b/components/Structures/Missions/Meteorologists/Cloudspotting/CloudSignal.tsx index fb92f33c..a60580c0 100644 --- a/components/Structures/Missions/Meteorologists/Cloudspotting/CloudSignal.tsx +++ b/components/Structures/Missions/Meteorologists/Cloudspotting/CloudSignal.tsx @@ -322,7 +322,7 @@ export default function CloudSignal() { onClick={() => removeCloud(cloud.id)} > - + ))} diff --git a/components/Structures/Missions/Meteorologists/Cloudspotting/CloudspottingOnMars.tsx b/components/Structures/Missions/Meteorologists/Cloudspotting/CloudspottingOnMars.tsx index 30abae11..26f97d63 100644 --- a/components/Structures/Missions/Meteorologists/Cloudspotting/CloudspottingOnMars.tsx +++ b/components/Structures/Missions/Meteorologists/Cloudspotting/CloudspottingOnMars.tsx @@ -1,9 +1,10 @@ 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 { CloudCogIcon, FolderCog, PaintBucket, Vote } from "lucide-react"; import { StarterLidar } from "@/components/Projects/Lidar/Clouds"; import VoteCoMClassifications from "./CoMVote"; +import CloudClassificationGenerator from "./CloudMaker"; interface Mission { id: number; @@ -75,13 +76,27 @@ const CloudspottingOnMars = () => { shadow: false, action: () => [], }, + { + id: 4, + chapter: 2, + title: "Create a cloud representation", + description: + "You can now add a visual representation of the cloud to your original classification", + icon: PaintBucket, + points: 1, + completedCount: 0, + internalComponent: () => , + color: 'text-green-300', + shadow: false, + action: () => [], + }, ]; }; useEffect(() => { if (!session) { return; - } + }; const fetchMissionPoints = async ( session: any, diff --git a/components/Structures/Missions/Meteorologists/Cloudspotting/CoMVote.tsx b/components/Structures/Missions/Meteorologists/Cloudspotting/CoMVote.tsx index 5c07d93f..7b935a2d 100644 --- a/components/Structures/Missions/Meteorologists/Cloudspotting/CoMVote.tsx +++ b/components/Structures/Missions/Meteorologists/Cloudspotting/CoMVote.tsx @@ -68,7 +68,7 @@ export default function VotePlanetClassifictions() { useEffect(() => { fetchClassifications(); - }, [session]) + }, [session]); const handleVote = async (classificationId: number, currentConfig: any) => { try { diff --git a/content/Posts/PostWithGen.tsx b/content/Posts/PostWithGen.tsx new file mode 100644 index 00000000..6cf9dd12 --- /dev/null +++ b/content/Posts/PostWithGen.tsx @@ -0,0 +1,150 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; +import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { ThumbsUp, MessageSquare } from "lucide-react"; +import { Badge } from "@/components/ui/badge"; +import { useSession, useSupabaseClient } from "@supabase/auth-helpers-react"; +import { CommentCard } from "../Comments/CommentSingle"; + +import CloudSignal from "@/components/Structures/Missions/Meteorologists/Cloudspotting/CloudSignal"; +import PlanetGenerator from "@/components/Data/Generator/Astronomers/PlanetHunters/PlanetGenerator"; + +interface CommentProps { + id: number; + author: string; + content: string; + created_at: string; +}; + +interface PostCardSingleProps { + classificationId: number; + title: string; + anomalyId: string; + author: string; + content: string; + votes: number; + category: string; + tags?: string[]; + classificationConfig?: any; + images: string[]; + classificationType: string; + onVote?: () => void; + children?: React.ReactNode; + commentStatus?: boolean; +}; + +export function PostCardSingleWithGenerator({ + classificationId, + title, + author, + content, + votes, + category, + tags, + anomalyId, + classificationConfig, + images, + classificationType, + commentStatus, + onVote, +}: PostCardSingleProps) { + const supabase = useSupabaseClient(); + const [comments, setComments] = useState([]); + const [loadingComments, setLoadingComments] = useState(true); + const [voteCount, setVoteCount] = useState(votes); + + useEffect(() => { + fetchComments(); + }, [classificationId]); + + const fetchComments = async () => { + setLoadingComments(true); + try { + const { data, error } = await supabase + .from("comments") + .select("*") + .eq("classification_id", classificationId) + .order("created_at", { ascending: false }); + + if (error) throw error; + setComments(data); + } catch (error) { + console.error("Error fetching comments:", error); + } finally { + setLoadingComments(false); + }; + }; + + const handleVoteClick = () => { + if (onVote) onVote(); + setVoteCount((prev) => prev + 1); + }; + + const renderDynamicComponent = () => { + switch (classificationType) { + case "cloud": + return ; + case "planet": + return ; + default: + return ( +
+ {loadingComments ? ( +

Loading comments...

+ ) : comments.length > 0 ? ( + comments.map((comment) => ( + + )) + ) : ( +

No comments yet. Be the first to comment!

+ )} +
+ ); + }; + }; + + return ( + + +
+ + + {author[0]} + +
+ {title} +

by {author}

+
+
+
+ + {category} +

{content}

+ {images.length > 0 && ( +
+ Post Image +
+ )} +
+ + + + + {renderDynamicComponent()} +
+ ); +}; \ No newline at end of file