Skip to content

Commit 8023752

Browse files
committed
🌂🧑🏻‍🦳 ↝ [SSG-90 SSG-91 SSC-54]: Cleaning up the post cards, improving DMP flow
1 parent ba71588 commit 8023752

File tree

9 files changed

+345
-162
lines changed

9 files changed

+345
-162
lines changed

app/tests/page.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ 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 DailyMinorPlanetMissions from "@/components/Structures/Missions/Astronomers/DailyMinorPlanet";
9+
import DailyMinorPlanetMissions from "@/components/Structures/Missions/Astronomers/DailyMinorPlanet/DailyMinorPlanet";
1010
// import { TopographicMap } from "@/components/topographic-map";
1111

1212
export default function TestPage() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
'use client';
2+
3+
import React, { useEffect, useState } from "react";
4+
import { PostCardSingle } from "@/content/Posts/PostSingle";
5+
import { useSession, useSupabaseClient } from "@supabase/auth-helpers-react";
6+
7+
interface Classification {
8+
id: number;
9+
created_at: string;
10+
content: string | null;
11+
author: string | null;
12+
anomaly: number | null;
13+
media: any | null;
14+
classificationtype: string | null;
15+
classificationConfiguration: any | null;
16+
};
17+
18+
export default function VoteDMPClassifications() {
19+
const supabase = useSupabaseClient();
20+
const session = useSession();
21+
22+
const [classifications, setClassifications] = useState<any[]>([]);
23+
const [loading, setLoading] = useState<boolean>(true);
24+
const [error, setError] = useState<string | null>(null);
25+
26+
const fetchClassifications = async () => {
27+
if (!session?.user) {
28+
setError("User session not found.");
29+
setLoading(false);
30+
return;
31+
}
32+
33+
setLoading(true);
34+
setError(null);
35+
try {
36+
const { data, error } = await supabase
37+
.from('classifications')
38+
.select('*')
39+
.eq('classificationtype', 'telescope-minorPlanet')
40+
.order('created_at', { ascending: false }) as { data: Classification[]; error: any };
41+
42+
if (error) throw error;
43+
44+
const processedData = data.map((classification) => {
45+
const media = classification.media;
46+
let images: string[] = [];
47+
48+
// Check if media is an array with at least 2 elements (array structure like [[], [...image URLs]])
49+
if (Array.isArray(media) && media.length > 1) {
50+
// Extract the image URLs from the second element if it's an array of URLs
51+
images = media[1] && Array.isArray(media[1]) ? media[1] : [];
52+
}
53+
// Check if there's a single image URL (media is not an array but an object with uploadUrl)
54+
else if (media && typeof media === "object" && media.uploadUrl) {
55+
images.push(media.uploadUrl);
56+
}
57+
58+
const votes = classification.classificationConfiguration?.votes || 0;
59+
60+
return { ...classification, images, votes };
61+
});
62+
63+
setClassifications(processedData);
64+
} catch (error) {
65+
console.error("Error fetching classifications:", error);
66+
setError("Failed to load classifications.");
67+
} finally {
68+
setLoading(false);
69+
}
70+
};
71+
72+
useEffect(() => {
73+
fetchClassifications();
74+
}, [session]);
75+
76+
const handleVote = async (classificationId: number, currentConfig: any) => {
77+
try {
78+
const currentVotes = currentConfig?.votes || 0;
79+
80+
const updatedConfig = {
81+
...currentConfig,
82+
votes: currentVotes + 1,
83+
};
84+
85+
const { error } = await supabase
86+
.from("classifications")
87+
.update({ classificationConfiguration: updatedConfig })
88+
.eq("id", classificationId);
89+
90+
if (error) {
91+
console.error("Error updating classificationConfiguration:", error);
92+
} else {
93+
setClassifications((prevClassifications) =>
94+
prevClassifications.map((classification) =>
95+
classification.id === classificationId
96+
? { ...classification, votes: updatedConfig.votes }
97+
: classification
98+
)
99+
);
100+
}
101+
} catch (error) {
102+
console.error("Error voting:", error);
103+
}
104+
};
105+
106+
return (
107+
<div className="space-y-8">
108+
{loading ? (
109+
<p>Loading classifications...</p>
110+
) : error ? (
111+
<p>{error}</p>
112+
) : (
113+
classifications.map((classification) => (
114+
<PostCardSingle
115+
key={classification.id}
116+
classificationId={classification.id}
117+
title={classification.title}
118+
author={classification.author}
119+
content={classification.content}
120+
votes={classification.votes || 0}
121+
category={classification.category}
122+
tags={classification.tags || []}
123+
images={classification.images || []}
124+
anomalyId={classification.anomaly}
125+
classificationConfig={classification.classificationConfiguration}
126+
classificationType={classification.classificationtype}
127+
onVote={() => handleVote(classification.id, classification.classificationConfiguration)}
128+
/>
129+
))
130+
)}
131+
</div>
132+
);
133+
};

components/Structures/Missions/Astronomers/DailyMinorPlanet.tsx components/Structures/Missions/Astronomers/DailyMinorPlanet/DailyMinorPlanet.tsx

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import { useEffect, useState } from "react";
22
import { useSupabaseClient, useSession } from "@supabase/auth-helpers-react";
33
import { RadioIcon, SpeechIcon, TelescopeIcon, VoteIcon } from "lucide-react";
4-
import MissionShell from "../BasePlate";
4+
import MissionShell from "../../BasePlate";
5+
import { DailyMinorPlanet } from "@/components/Projects/Telescopes/DailyMinorPlanet";
6+
import VoteDMPClassifications from "./DMPVote";
57

68
const DailyMinorPlanetMissions = () => {
79
const supabase = useSupabaseClient();
@@ -15,7 +17,7 @@ const DailyMinorPlanetMissions = () => {
1517
icon: TelescopeIcon,
1618
points: 2,
1719
completedCount: 0,
18-
internalComponent: () => <div>Asteroid classification component here</div>,
20+
internalComponent: () => <DailyMinorPlanet />,
1921
color: "text-blue-500",
2022
shadow: false,
2123
action: () => {},
@@ -27,7 +29,7 @@ const DailyMinorPlanetMissions = () => {
2729
icon: RadioIcon,
2830
points: 1,
2931
completedCount: 0,
30-
internalComponent: () => <div>Classification proposal component here</div>,
32+
internalComponent: () => <DailyMinorPlanet />,
3133
color: "text-green-500",
3234
shadow: false,
3335
action: () => {},
@@ -40,7 +42,7 @@ const DailyMinorPlanetMissions = () => {
4042
icon: SpeechIcon,
4143
points: 1,
4244
completedCount: 0,
43-
internalComponent: () => <div></div>,
45+
internalComponent: () => <VoteDMPClassifications />,
4446
color: "text-cyan-600",
4547
shadow: true,
4648
action: () => {},

components/Structures/Missions/Astronomers/PlanetHunters/PHVote.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import StarnetLayout from "@/components/Layout/Starnet";
88
interface Classification {
99
id: number;
1010
created_at: string;
11-
content: string | null;
11+
content: string | null;
1212
author: string | null;
1313
anomaly: number | null;
1414
media: any | null;
+83-77
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import { useState } from "react";
2-
import { Button } from "@/components/ui/button";
3-
import { Card, CardContent } from "@/components/ui/card";
2+
import { Button } from "@/components/ui/button";
43
import { motion } from "framer-motion";
5-
import { AnimatePresence } from "framer-motion";
4+
import { AnimatePresence } from "framer-motion";
65

76
interface MissionConfig {
87
id: number;
@@ -17,87 +16,94 @@ interface MissionConfig {
1716
}
1817

1918
interface MissionShellProps {
20-
missions: MissionConfig[];
21-
experiencePoints: number;
22-
level: number;
23-
currentChapter: number;
19+
missions: MissionConfig[];
20+
experiencePoints: number;
21+
level: number;
22+
currentChapter: number;
2423
}
2524

2625
const MissionShell = ({ missions, experiencePoints, level, currentChapter }: MissionShellProps) => {
27-
const [selectedMission, setSelectedMission] = useState<MissionConfig | null>(null);
28-
29-
const renderMission = (mission: MissionConfig) => {
30-
const completedCount = mission.completedCount ?? 0;
31-
32-
return (
33-
<div
34-
key={mission.id}
35-
className={`flex items-center p-6 rounded-2xl cursor-pointer${
36-
mission.id > 2 ? "bg-[#74859A]" : mission.id < 3 ? "bg-gray-000" : completedCount > 0 ? "bg-gray-700" : ""
37-
}`}
38-
onClick={() => setSelectedMission(mission)}
39-
>
40-
<mission.icon className={`w-10 h-10 ${mission.color}`} />
41-
<div className="ml-4">
42-
<h2 className={`text-lg font-bold ${mission.color}`}>{mission.title}</h2>
43-
<p className={`text-sm ${mission.color}`}>{mission.description}</p>
44-
<p className={`text-sm ${mission.color}`}>Points: {mission.points}</p>
45-
</div>
46-
<div className="ml-auto text-right">
47-
<p className="text-xs">Completed: {completedCount}</p>
48-
<p className="text-xl font-bold">{completedCount}</p>
49-
</div>
50-
</div>
51-
);
52-
};
53-
26+
const [selectedMission, setSelectedMission] = useState<MissionConfig | null>(null);
27+
28+
const renderMission = (mission: MissionConfig) => {
29+
const completedCount = mission.completedCount ?? 0;
30+
5431
return (
55-
<div className="flex flex-col items-center bg-[#1D2833] text-white rounded-2xl shadow-lg p-6 w-full max-w-4xl mx-auto">
56-
<div className="flex justify-between w-full mb-6">
57-
<h1 className="text-xl font-bold">Chapter {currentChapter}</h1>
58-
</div>
59-
<div className="flex-1 overflow-y-auto w-full">
60-
<div className="w-full bg-gray-700 rounded-full h-4 mb-6">
61-
<div
62-
className="bg-[#5FCBC3] h-4 rounded-full"
63-
style={{ width: `${(experiencePoints % 9) * 10.5}%` }}
64-
></div>
65-
</div>
32+
<div
33+
key={mission.id}
34+
className={`flex items-center p-6 rounded-2xl cursor-pointer${
35+
mission.id > 2
36+
? "bg-[#74859A]"
37+
: mission.id < 3
38+
? "bg-gray-000"
39+
: completedCount > 0
40+
? "bg-gray-700"
41+
: ""
42+
}`}
43+
onClick={() => setSelectedMission(mission)}
44+
>
45+
<mission.icon className={`w-10 h-10 ${mission.color}`} />
46+
<div className="ml-4">
47+
<h2 className={`text-lg font-bold ${mission.color}`}>{mission.title}</h2>
48+
<p className={`text-sm ${mission.color}`}>{mission.description}</p>
49+
<p className={`text-sm ${mission.color}`}>Points: {mission.points}</p>
6650
</div>
67-
<p className="text-sm text-center mb-6">
68-
Level {level} ({experiencePoints} points)
69-
</p>
70-
<div className="bg-gray-700 p-6 rounded-2xl w-full mb-6">
71-
<div className="grid grid-cols-2 gap-4 w-full">
72-
{missions.slice(0, 2).map((mission) => renderMission(mission))}
73-
</div>
51+
<div className="ml-auto text-right">
52+
<p className="text-xs">Completed: {completedCount}</p>
53+
<p className="text-xl font-bold">{completedCount}</p>
7454
</div>
75-
<div className="grid gap-4 w-full mt-6">
76-
{missions.slice(2).map((mission) => renderMission(mission))}
77-
</div>
78-
<AnimatePresence>
79-
{selectedMission && (
80-
<motion.div
81-
className="fixed inset-0 flex justify-center items-center bg-black bg-opacity-50"
82-
initial={{ opacity: 0, y: -10 }}
83-
animate={{ opacity: 1, y: 0 }}
84-
exit={{ opacity: 0, y: -10 }}
85-
>
86-
<div className="bg-[#2C4F64] p-6 rounded-lg max-w-md w-full">
87-
<h3 className="text-xl font-semibold mb-2">{selectedMission.title}</h3>
88-
<p>{selectedMission.description}</p>
89-
<div className="mt-4">
90-
{selectedMission.internalComponent && <selectedMission.internalComponent />}
91-
</div>
92-
<Button onClick={() => setSelectedMission(null)} className="mt-4">
93-
Close
94-
</Button>
95-
</div>
96-
</motion.div>
97-
)}
98-
</AnimatePresence>
9955
</div>
10056
);
57+
};
58+
59+
return (
60+
<div className="flex flex-col items-center bg-[#1D2833] text-white rounded-2xl shadow-lg p-6 w-full max-w-4xl mx-auto">
61+
{!selectedMission && (
62+
<>
63+
<div className="flex justify-between w-full mb-6">
64+
<h1 className="text-xl font-bold">Chapter {currentChapter}</h1>
65+
</div>
66+
<div className="flex-1 overflow-y-auto w-full">
67+
<div className="w-full bg-gray-700 rounded-full h-4 mb-6">
68+
<div
69+
className="bg-[#5FCBC3] h-4 rounded-full"
70+
style={{ width: `${(experiencePoints % 9) * 10.5}%` }}
71+
></div>
72+
</div>
73+
</div>
74+
<p className="text-sm text-center mb-6">
75+
Level {level} ({experiencePoints} points)
76+
</p>
77+
<div className="bg-gray-700 p-6 rounded-2xl w-full mb-6">
78+
<div className="grid grid-cols-2 gap-4 w-full">
79+
{missions.slice(0, 2).map((mission) => renderMission(mission))}
80+
</div>
81+
</div>
82+
<div className="grid gap-4 w-full mt-6">
83+
{missions.slice(2).map((mission) => renderMission(mission))}
84+
</div>
85+
</>
86+
)}
87+
<AnimatePresence>
88+
{selectedMission && (
89+
<motion.div
90+
className="flex flex-col bg-[#1D2833]"
91+
initial={{ opacity: 0 }}
92+
animate={{ opacity: 1 }}
93+
exit={{ opacity: 0 }}
94+
>
95+
<div className="flex justify-between items-center p-4">
96+
<h3 className="text-xl font-semibold">{selectedMission.title}</h3>
97+
<Button onClick={() => setSelectedMission(null)}>Back</Button>
98+
</div>
99+
<div className="flex-1 overflow-auto">
100+
{selectedMission.internalComponent && <selectedMission.internalComponent />}
101+
</div>
102+
</motion.div>
103+
)}
104+
</AnimatePresence>
105+
</div>
106+
);
101107
};
102-
108+
103109
export default MissionShell;

constants/Structures/Properties.tsx

+4-2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import FreeformUploadData from "@/components/Projects/(classifications)/FreeForm
2929
import { ZoodexIguanas } from "@/components/Projects/Zoodex/iguanasFromAbove";
3030
import PlanetHuntersSteps from "@/components/Structures/Missions/Astronomers/PlanetHunters/PlanetHunters";
3131
import { useRouter } from 'next/router';
32+
import DailyMinorPlanetMissions from "@/components/Structures/Missions/Astronomers/DailyMinorPlanet/DailyMinorPlanet";
3233

3334
interface IndividualStructureProps {
3435
name?: string;
@@ -190,8 +191,9 @@ export const StructuresConfig: StructureConfig = {
190191
{
191192
icon: <MoonStarIcon className="w-6 h-6 text-[#5e81ac]" />,
192193
text: "Discover asteroids",
193-
dynamicComponent: <DailyMinorPlanet />,
194-
sizePercentage: 50,
194+
// dynamicComponent: <DailyMinorPlanet />,
195+
dynamicComponent: <DailyMinorPlanetMissions />,
196+
sizePercentage: 95,
195197
},
196198
// {
197199
// icon: <StarIcon className="w-6 h-6 text-[#5e81ac]" />,

0 commit comments

Comments
 (0)