Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
8c350a4
uh
hanaeatsplanes Nov 25, 2025
59ae6ad
uh
hanaeatsplanes Nov 25, 2025
c404dbe
yes.
hanaeatsplanes Nov 25, 2025
01283d8
upgrade
hanaeatsplanes Nov 25, 2025
b40d8af
be scared.
hanaeatsplanes Nov 25, 2025
9870653
app routing it
hanaeatsplanes Nov 30, 2025
9e4a6f4
Merge remote-tracking branch 'origin/main' into explodes
hanaeatsplanes Nov 30, 2025
6cdcf50
hana fixes buttoms
hanaeatsplanes Nov 30, 2025
dd2cbd9
uwu
hanaeatsplanes Nov 30, 2025
dae501a
uwu
hanaeatsplanes Dec 4, 2025
8f6a80d
merged that conflict
hanaeatsplanes Dec 4, 2025
5988565
update next dependency to use caret version and reorder next-auth in …
hanaeatsplanes Dec 10, 2025
6ee74aa
uh.
hanaeatsplanes Dec 12, 2025
a92425a
update
hanaeatsplanes Dec 12, 2025
802a31c
refactor layout and page components; remove unused imports and simpli…
hanaeatsplanes Dec 12, 2025
f985d2e
fix Navbar z-index value for improved stacking context
hanaeatsplanes Dec 12, 2025
003efe5
update devDependencies: bump tailwindcss and @types/node versions; ad…
hanaeatsplanes Dec 12, 2025
417ea98
so uh jsx does not work like that
hanaeatsplanes Dec 12, 2025
fc9b985
asdkajdskajdakjsk login!!!??!!?!?!??!???!!?!?!!!!!
hanaeatsplanes Dec 16, 2025
90cdf56
gleebus
hanaeatsplanes Dec 17, 2025
a6b6763
:explodes:
hanaeatsplanes Dec 17, 2025
eb41a70
clean up clean up everybody clap your hands
hanaeatsplanes Dec 17, 2025
fefb2af
guh.
hanaeatsplanes Dec 19, 2025
1b1f6ff
i am bad at js (very bad)
hanaeatsplanes Dec 19, 2025
0c34542
bleh
hanaeatsplanes Dec 19, 2025
ad5a554
implement user authentication and context provider in dashboard layout
hanaeatsplanes Dec 21, 2025
0e650ec
bwgh
hanaeatsplanes Dec 22, 2025
5e3883b
befhahsda
hanaeatsplanes Dec 22, 2025
f302661
uh
hanaeatsplanes Dec 22, 2025
39b2765
woke no redirect for u heheheh
hanaeatsplanes Dec 22, 2025
c00fae5
bleghasda
hanaeatsplanes Dec 22, 2025
1385234
me when react:
hanaeatsplanes Dec 22, 2025
212c68f
uh so /dashboard autoreditecsts so iguess w'erisb doign that nwo
hanaeatsplanes Dec 22, 2025
2f40f5a
update dependencies and enhance project layout with new ProjectCard c…
hanaeatsplanes Dec 24, 2025
9236222
Add project creation functionality and enhance dashboard layout
jleuth Jan 4, 2026
5936b2f
eadsakdask
hanaeatsplanes Jan 4, 2026
72080ac
Add devlog creation and display functionality with new components for…
jleuth Jan 4, 2026
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
File renamed without changes.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,4 @@ next-env.d.ts
/.idea/*
/.idea/
/src/dev/
/backend/
230 changes: 134 additions & 96 deletions bun.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import type { NextConfig } from "next";

const nextConfig: NextConfig = {
/* config options here */
reactStrictMode: true,
reactStrictMode: true
};


module.exports = {
images: {
remotePatterns: [
Expand Down
14 changes: 8 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,23 @@
},
"dependencies": {
"clsx": "^2.1.1",
"next": "15.5.7",
"next": "^16.1.1",
"next-auth": "^4.24.13",
Copy link

Copilot AI Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using next-auth 4.x with Next.js 16.x may have compatibility issues. The next-auth package is at version 4.24.13, but the stable version recommended for Next.js App Router (v13+) is NextAuth.js v5 (Auth.js). Consider upgrading to the latest Auth.js to ensure full compatibility with Next.js 16 and the App Router architecture.

Suggested change
"next-auth": "^4.24.13",
"@auth/nextjs": "^0.8.2",
"@auth/core": "^0.37.2",

Copilot uses AI. Check for mistakes.
"react": "19.2.0",
"react-dom": "19.2.0",
"react-icons": "^5.5.0",
"swr": "^2.3.7"
"swr": "^2.3.8"
},
"devDependencies": {
"@eslint/eslintrc": "^3.3.3",
"@tailwindcss/postcss": "^4.1.17",
"@types/node": "^20.19.25",
"@tailwindcss/postcss": "^4.1.18",
"@types/bun": "^1.3.5",
"@types/node": "^20.19.27",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
"eslint": "^9.39.1",
"eslint": "^9.39.2",
"eslint-config-next": "15.5.6",
"tailwindcss": "^4.1.17",
"tailwindcss": "^4.1.18",
"typescript": "^5.9.3"
}
}
Binary file added public/aces_new.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/bg_new.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/shehasnoclueeither.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions src/app/api/dashboard/devlogs/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { NextRequest, NextResponse } from "next/server";

export async function POST(req: NextRequest) {
const sessionId = req.cookies.get("sessionId")?.value;

if (!sessionId) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}

const data = await req.json();

const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/v1/devlogs/`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Cookie: `sessionId=${sessionId}`,
},
body: JSON.stringify(data),
});

const responseData = await res.json().catch(() => null);

return NextResponse.json(responseData, { status: res.status });
}
41 changes: 41 additions & 0 deletions src/app/api/dashboard/login/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { NextRequest, NextResponse } from "next/server";

export async function POST(req: NextRequest) {
const data = await req.json();
const { email, otp } = data;

const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/v1/auth/validate_otp`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ email, otp }),
credentials: "include",
});

let cookie: string | null = null;

try {
const data = await res.json();
cookie = data?.sessionId ?? null;
} catch {
// ignore
Comment on lines +21 to +22
Copy link

Copilot AI Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing error handling for the catch block. The try-catch at line 21 catches errors but doesn't handle them - it just has an empty comment. This means parsing errors will be silently ignored. Consider logging the error or setting a default value with proper error handling.

Suggested change
} catch {
// ignore
} catch (error) {
console.error("Failed to parse validate_otp response JSON:", error);
cookie = null;

Copilot uses AI. Check for mistakes.
}

const response = new NextResponse(null, {
status: res.status,
});

if (cookie) {
response.cookies.set({
name: "sessionId",
value: cookie,
httpOnly: true,
secure: true,
path: '/',
sameSite: 'none',
Copy link

Copilot AI Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing max-age or expires attribute for the session cookie. The cookie configuration sets httpOnly, secure, path, and sameSite but doesn't specify a max-age or expires attribute. This means the cookie will be a session cookie that gets deleted when the browser closes. If persistent login is desired, add a maxAge attribute.

Suggested change
sameSite: 'none',
sameSite: 'none',
maxAge: 60 * 60 * 24 * 7, // 7 days

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sameSite cookie attribute is set to 'none' which requires the secure flag. While secure is set to true, this configuration means cookies will be sent in cross-site requests. This could be a security risk if not intentional. If the frontend and API are on the same domain, consider using 'lax' or 'strict' instead for better security.

Suggested change
sameSite: 'none',
sameSite: 'lax',

Copilot uses AI. Check for mistakes.
});
}

return response;
}
24 changes: 24 additions & 0 deletions src/app/api/dashboard/projects/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { NextRequest, NextResponse } from "next/server";

export async function POST(req: NextRequest) {
const sessionId = req.cookies.get("sessionId")?.value;

if (!sessionId) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}

const data = await req.json();

const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/v1/projects/`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Cookie: `sessionId=${sessionId}`,
},
body: JSON.stringify(data),
});

const responseData = await res.json().catch(() => null);

return NextResponse.json(responseData, { status: res.status });
}
19 changes: 19 additions & 0 deletions src/app/api/dashboard/request_otp/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { NextRequest } from "next/server";

export async function POST(req: NextRequest) {
const data = await req.json();
const email = data.email;

const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/v1/auth/send_otp`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ email }),
});

return new Response(null,{
status: res.status,
headers: { "Content-Type": "application/json" },
})
}
27 changes: 27 additions & 0 deletions src/app/api/git/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { NextResponse } from "next/server";

let cache: {
hash: string
message: string
} | undefined

export async function GET() {
if (!cache) {
const githubRes = await fetch(
"https://api.github.com/repos/hackclub/aces/commits/main"
)

if (!githubRes.ok) {
throw new Error(`GitHub API error: ${githubRes.status}`)
}

const json = await githubRes.json()

cache = {
hash: json.sha.substring(0, 7),
message: json.commit.message,
}
}

return NextResponse.json(cache)
}
24 changes: 24 additions & 0 deletions src/app/api/rsvp/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { NextResponse } from "next/server";

const cacheForEmergencies: number | undefined = undefined

export async function GET() {
try {
const res = await fetch(
`https://api.airtable.com/v0/${process.env.RSVP_AIRTABLE_BASE_ID}/RSVPS?sort[0][field]=count&sort[0][direction]=desc&maxRecords=1`,
{
headers: {
Authorization: `Bearer ${process.env.RSVP_AIRTABLE_API_KEY}`,
},
}
);

const data = await res.json();
const newestRecord = data.records?.[0];
const count = newestRecord?.fields?.count ?? cacheForEmergencies;

return NextResponse.json({ count });
} catch {
return NextResponse.json({ count: cacheForEmergencies, error: "Failed to fetch" }, { status: 500 });
}
}
12 changes: 12 additions & 0 deletions src/app/dashboard/UserProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"use client";
import { ReactNode } from "react";
import { UserContext } from "@/app/dashboard/userContext";
import { User } from "@/app/dashboard/layout";

export default function UserProvider({ user, children }: { user: User; children: ReactNode }) {
return (
<UserContext value={user}>
{children}
</UserContext>
Comment on lines +8 to +10
Copy link

Copilot AI Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The UserContext is being used incorrectly. UserContext.Provider requires a value prop, but the code uses just value={user} without the .Provider suffix. This should be <UserContext.Provider value={user}> instead of <UserContext value={user}>.

Suggested change
<UserContext value={user}>
{children}
</UserContext>
<UserContext.Provider value={user}>
{children}
</UserContext.Provider>

Copilot uses AI. Check for mistakes.
);
}
61 changes: 61 additions & 0 deletions src/app/dashboard/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { ReactNode } from "react";
import { cookies } from "next/headers";
import { redirect } from "next/navigation";
import DashNav from "@/components/dashboard/DashNav";
import UserProvider from "@/app/dashboard/UserProvider";

export type User = {
id: number
email: string
username: string
hackatime_id: number
permissions: string[]
marked_for_deletion: boolean
} | null

async function getUser(): Promise<User> {
const cookieStore = await cookies();
const sessionId = cookieStore.get("sessionId")?.value;

if (!sessionId) {
return null;
}

const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/v1/users/me`, {
headers: {
Cookie: `sessionId=${sessionId}`,
},
cache: "no-store",
});

if (res.status === 401) {
return null;
}

if (!res.ok) {
return null;
}

return res.json();
}

export default async function DashboardLayout({ children }: { children: ReactNode }) {
const user = await getUser();

if (!user) {
redirect("/login");
}

return (
<div className="h-screen w-screen flex flex-col bg-[url(/bg_new.png)] bg-cover">
<div className="flex-1 p-4 pb-24 md:pb-32 lg:pb-40 overflow-auto">
<div className="bg-red-300/50 backdrop-blur-md rounded-lg shadow-lg p-6 min-h-full">
<UserProvider user={user}>
{children}
</UserProvider>
</div>
</div>
<DashNav />
</div>
);
}
71 changes: 71 additions & 0 deletions src/app/dashboard/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { cookies } from "next/headers";
import Link from "next/link";
import ProjectCard from "@/components/dashboard/ProjectCard";

export type Project = {
project_id: number;
project_name: string;
hackatime_projects: string[];
hackatime_total_hours: number;
last_updated: string;
repo: string;
demo_url: string;
preview_image: string;
shipped: boolean;
};

async function getProjects(): Promise<Project[]> {
const cookieStore = await cookies();
const sessionId = cookieStore.get("sessionId")?.value;
console.log(sessionId);

if (!sessionId) {
return [];
}

const req = new Request(
`${process.env.NEXT_PUBLIC_API_URL}/api/v1/projects`,
{
method: "GET",
headers: {
Cookie: `sessionId=${sessionId}`,
},
cache: "no-store",
}
)

console.log(req)

const res = await fetch(req);

console.log(res)

if (!res.ok) {
return [];
}

return res.json();
}

export default async function Page() {
const projects = await getProjects();

return (
<div>
<h1 className="text-3xl font-bold mb-6">My Projects</h1>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<Link href="/dashboard/projects/new">
<div className="rounded-lg overflow-hidden shadow-md hover:shadow-lg transition-shadow bg-gray-50 h-full min-h-[200px] flex items-center justify-center cursor-pointer border-2 border-dashed border-gray-300 hover:border-gray-400">
<div className="text-center p-4">
<span className="text-4xl text-gray-400">+</span>
<p className="text-gray-600 mt-2">New Project</p>
</div>
</div>
</Link>
{projects.map((project) => (
<ProjectCard key={project.project_id} project={project} />
))}
</div>
</div>
);
}
Loading