Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions app/api/scores/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// app/api/scores/route.ts
import { NextResponse } from 'next/server';
import mongoose from 'mongoose';
import { Score } from '@/app/models/Score'; // Adjust the import path if necessary

const DB_URI = process.env.MONGO_URI;

const connectToDatabase = async () => {
try {
if (mongoose.connection.readyState >= 1) {
return; // already connected
}
await mongoose.connect(DB_URI!, { useNewUrlParser: true, useUnifiedTopology: true });
console.log('Connected to MongoDB');
} catch (err) {
console.error('MongoDB connection error:', err);
}
};

// Handle POST request to save a score
export async function POST(req: Request) {
await connectToDatabase(); // Ensure DB is connected

try {
const { name, score } = await req.json(); // Get data from the request body

// Create a new score document
const newScore = new Score({ name, score });
await newScore.save();

return NextResponse.json({
message: 'Score saved successfully!',
score: newScore,
});
} catch (err) {
console.error('Error saving score:', err);
return NextResponse.json(
{ error: 'Failed to save the score' },
{ status: 500 }
);
}
}

// Handle GET request to fetch all scores
export async function GET(req: Request) {
await connectToDatabase();

try {
const scores = await Score.find();
return NextResponse.json(scores);
} catch (err) {
console.error('Error retrieving scores:', err);
return NextResponse.json(
{ error: 'Failed to retrieve scores' },
{ status: 500 }
);
}
}
27 changes: 27 additions & 0 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,33 @@
}
}

.fancySchmancyButton{
border-radius: 10px;
background-color: white;
width: 140px;
height: 70px;

text-align: center;
outline: none;
letter-spacing: 2px;
word-spacing: 6px;
line-height: 1.5;
text-transform: uppercase;
align-self: center;
margin-bottom: 10px;
transition-duration: 0.2s;
}

.fancySchmancyButton:hover{
transform: scale(1.2);
outline: 3px;
border: 3px white;
background-color: transparent;
color:white;
transition-duration: 0.2s;

}

.bg {
background: url("/backgroundImage.jpg") no-repeat;
background-size: cover;
Expand Down
19 changes: 19 additions & 0 deletions app/models/Score.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// models/Score.ts
import mongoose, { Document, Schema, Model } from 'mongoose';

// Define the TypeScript interface for the Score model
interface IScore extends Document {
name: string;
score: number;
}

// Create the Mongoose schema for the Score model
const scoreSchema: Schema = new Schema({
name: { type: String, required: true },
score: { type: Number, required: true },
});

// Create the Mongoose model for the Score
const Score: Model<IScore> = mongoose.models.Score || mongoose.model<IScore>('Score', scoreSchema);

export { Score };
80 changes: 80 additions & 0 deletions app/surfers/leaderboard/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
"use client";

import { useState, useEffect } from "react";
import NavBar from "@/components/nav-bar/nav-bar";
import "@/app/globals.css";
import HackRPIButton from "@/components/themed-components/hackrpi-button";

const isDirector = false; // Set to `true` to test the director functionality

interface Result {
id: string;
name: string;
score: number;
}

export default function Page() {
const [leaderboardEntries, setLeaderboardEntries] = useState<Result[]>([]);

// Function to fetch leaderboard entries
const fetchLeaderboard = async () => {
try {
const response = await fetch("http://localhost:3000/api/scores");
if (response.ok) {
const data: Result[] = await response.json(); // Ensure the data is typed as Result[]
setLeaderboardEntries(data); // Update state with the fetched leaderboard data
} else {
console.error("Failed to fetch leaderboard data");
}
} catch (error) {
console.error("Error fetching leaderboard:", error);
}
};

useEffect(() => {
fetchLeaderboard(); // Fetch leaderboard when the component mounts
}, []); // Empty dependency array ensures this runs once when the component mounts

return (
<div className="flex flex-col items-center justify-start w-full h-full">
<div className="flex-grow flex-shrink basis-auto">
<h1 className="mt-28 text-center text-4xl font-modern text-hackrpi-orange">2048 Leaderboard</h1>
<table className="min-w-[80vw] mt-10 justify-inbetween table-auto w-full table table-zebra">
<thead>
<tr className="text-white bg-hackrpi-yellow">
<th className="w-1/4 px-4 py-2 text-center font-retro text-white">Position</th>
<th className="w-1/3 px-4 py-2 text-center font-retro text-white">Username</th>
<th className="w-1/3 px-4 py-2 text-center font-retro text-white">Score</th>
{isDirector ? (
<th className="w-1/3 px-4 py-2 font-retro text-white bg-hackrpi-yellow">Delete</th>
) : null}
</tr>
</thead>

<tbody className="text-center text-white font-retro bg-gradient-to-r from-hackrpi-dark-purple to-hackrpi-yellow">
{leaderboardEntries.map((entry, index) => (
<tr key={entry.id}>
<td className="px-y py-2">{index + 1}</td>
<td className="px-4 py-2">{entry.name}</td>
<td className="px-4 py-2">{entry.score}</td>
{isDirector ? (
<td className="px-4 py-2 flex items-center justify-center">
<HackRPIButton
onClick={async () => {
alert("not implemented yet");
}}
>
Delete Item
</HackRPIButton>
</td>
) : null}
</tr>
))}
</tbody>
</table>
</div>
<div className="flex-grow mt-24"></div>
<div className="absolute-bottom-0 w-full"></div>
</div>
);
}
Loading
Loading