Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
cb8d48b
feat(profile): updates and enhance notification settings management
SebasianDev Feb 4, 2026
a67bf1e
fix(SocialLogin): update social media provider from 'X' to 'twitter' …
SebasianDev Feb 5, 2026
b9cfc54
feat(set-winner): enhance project winner setting logic with error han…
SebasianDev Feb 5, 2026
b605cd3
Merge branch 'master' of https://github.com/Voyager-Ship/avalanche-do…
SebasianDev Feb 10, 2026
92c76e1
Merge branch 'master' of https://github.com/Voyager-Ship/avalanche-do…
SebasianDev Feb 17, 2026
e05b9be
fix: enhance WalletConnectButton to use a stable account request method
SebasianDev Feb 17, 2026
f354c91
feat: implement profile completion percentage calculation and integra…
SebasianDev Feb 17, 2026
853866d
refactor: replace authentication redirection with login modal trigger…
SebasianDev Feb 17, 2026
7f22132
fix: update wallet label in profile component to specify EVM Wallet
SebasianDev Feb 17, 2026
fca801a
refactor: streamline image handling in reward components and enhance …
SebasianDev Feb 18, 2026
1655353
feat: add website and socials fields to project submission form
SebasianDev Feb 18, 2026
7e4e560
feat: integrate UserAvatarContext for enhanced avatar management acro…
SebasianDev Feb 18, 2026
eda77bc
feat: enhance registration form with profile integration and field pe…
SebasianDev Feb 18, 2026
4dd4d08
fix: update profile form label from "City of Residence" to "Country"
SebasianDev Feb 18, 2026
813466c
refactor: improve avatar section in ProfileHeader for better accessib…
SebasianDev Feb 18, 2026
e54dfe5
refactor: enforce name validation in profile and registration forms
SebasianDev Feb 18, 2026
feb3b06
refactor: update form validation mode to 'onChange' in profile setup …
SebasianDev Feb 18, 2026
241de66
refactor: update styling for various components to improve layout and…
SebasianDev Feb 18, 2026
b0ec93f
refactor: update project submission handling to use Prisma.JsonNull f…
SebasianDev Feb 18, 2026
fb0036d
refactor: update import paths and comment out unused components in pr…
SebasianDev Feb 18, 2026
ed2922c
refactor: update API route handlers to use Session type and improve t…
SebasianDev Feb 19, 2026
d5d1bba
refactor: enhance error handling in project winner API and improve ty…
SebasianDev Feb 19, 2026
ee17669
Merge branch 'master' of https://github.com/Voyager-Ship/avalanche-do…
SebasianDev Feb 25, 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
11 changes: 6 additions & 5 deletions app/(home)/showcase/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import React from "react";
import { redirect } from "next/navigation";
import ProjectOverview from "../../../../components/showcase/ProjectOverview";
import { getProject } from "@/server/services/projects";
import { Project } from "@/types/showcase";
import { getUserBadgesByProjectId } from "@/server/services/project-badge";
import { ShowcaseProjectAuthWrapper } from "@/components/showcase/ShowcaseProjectAuthWrapper";
import { getAuthSession } from "@/lib/auth/authSession";

export default async function ProjectPage({
Expand Down Expand Up @@ -50,8 +49,10 @@ export default async function ProjectPage({
const badges = await getUserBadgesByProjectId(id);

return (
<main className="container relative max-w-[1400px] pb-16">
<ProjectOverview project={project as unknown as Project} badges={badges} />
</main>
<ShowcaseProjectAuthWrapper
project={project}
badges={badges}
projectId={id}
/>
);
}
11 changes: 5 additions & 6 deletions app/(home)/student-launchpad/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,26 @@ import { useTheme } from 'next-themes'
import Link from 'next/link'
import { useState, useEffect } from 'react'
import { useSession } from 'next-auth/react'
import { useRouter } from 'next/navigation'
import { useLoginModalTrigger } from '@/hooks/useLoginModal'

export default function StudentLaunchpadPage() {
const { resolvedTheme } = useTheme()
const arrowColor = resolvedTheme === "dark" ? "white" : "black"
const [iframeLoaded, setIframeLoaded] = useState(false)
const { data: session, status } = useSession()
const router = useRouter()
const { openLoginModal } = useLoginModalTrigger()

const handleIframeLoad = () => {
setIframeLoaded(true)
}

// Redirect to login if not authenticated
// Show login modal if not authenticated
useEffect(() => {
if (status === 'unauthenticated') {
const currentUrl = window.location.href
const loginUrl = `/login?callbackUrl=${encodeURIComponent(currentUrl)}`
router.push(loginUrl)
openLoginModal(currentUrl)
}
}, [status, router])
}, [status, openLoginModal])

// Show loading state while checking authentication
if (status === 'loading') {
Expand Down
15 changes: 8 additions & 7 deletions app/api/profile/[id]/route.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { NextRequest, NextResponse } from 'next/server';
import { Session } from 'next-auth';
import { getProfile, updateProfile } from '@/server/services/profile';
import { Profile } from '@/types/profile';
import { withAuth } from '@/lib/protectedRoute';
import { withAuth, RouteParams } from '@/lib/protectedRoute';

export const GET = withAuth(async (
export const GET = withAuth<RouteParams<{ id: string }>>(async (
req: NextRequest,
{ params }: { params: Promise<{ id: string }> },
session: any
{ params },
session: Session
) => {
try {
const id = (await params).id;
Expand Down Expand Up @@ -36,10 +37,10 @@ export const GET = withAuth(async (
}
});

export const PUT = withAuth(async (
export const PUT = withAuth<RouteParams<{ id: string }>>(async (
req: NextRequest,
{ params }: { params: Promise<{ id: string }> },
session: any
{ params },
session: Session
) => {
try {
const id = (await params).id;
Expand Down
73 changes: 64 additions & 9 deletions app/api/profile/extended/[id]/route.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import { NextRequest, NextResponse } from 'next/server';
import {
getExtendedProfile,
import { Session } from 'next-auth';
import {
getExtendedProfile,
updateExtendedProfile,
ProfileValidationError
} from '@/server/services/profile/profile.service';
import { UpdateExtendedProfileData } from '@/types/extended-profile';
import { withAuth } from '@/lib/protectedRoute';
import { withAuth, RouteParams } from '@/lib/protectedRoute';

/**
* GET /api/profile/extended/[id]
* Gets the extended profile of a user
*/
export const GET = withAuth(async (
export const GET = withAuth<RouteParams<{ id: string }>>(async (
req: NextRequest,
{ params }: { params: Promise<{ id: string }> },
session: any
{ params },
session: Session
) => {
try {
const id = (await params).id;
Expand Down Expand Up @@ -62,10 +63,10 @@ export const GET = withAuth(async (
* PUT /api/profile/extended/[id]
* update extended profile
*/
export const PUT = withAuth(async (
export const PUT = withAuth<RouteParams<{ id: string }>>(async (
req: NextRequest,
{ params }: { params: Promise<{ id: string }> },
session: any
{ params },
session: Session
) => {
try {
const id = (await params).id;
Expand Down Expand Up @@ -113,3 +114,57 @@ export const PUT = withAuth(async (
}
});

/**
* PATCH /api/profile/extended/[id]
* Partial update of extended profile (useful for settings updates)
*/
export const PATCH = withAuth<RouteParams<{ id: string }>>(async (
req: NextRequest,
{ params },
session: Session
) => {
try {
const id = (await params).id;

if (!id) {
return NextResponse.json(
{ error: 'User ID is required.' },
{ status: 400 }
);
}

// verify that the user can only update their own profile
if (session.user.id !== id) {
return NextResponse.json(
{ error: 'Forbidden: You can only update your own profile.' },
{ status: 403 }
);
}

const newProfileData = (await req.json()) as UpdateExtendedProfileData;

// The service now handles all business validations
const updatedProfile = await updateExtendedProfile(id, newProfileData);

return NextResponse.json(updatedProfile);
} catch (error) {
console.error('Error in PATCH /api/profile/extended/[id]:', error);

// Handle validation errors with the appropriate status code
if (error instanceof ProfileValidationError) {
return NextResponse.json(
{ error: error.message },
{ status: error.statusCode }
);
}

// Handle other errors
return NextResponse.json(
{
error: 'Internal Server Error',
details: error instanceof Error ? error.message : 'Unknown error'
},
{ status: 500 }
);
}
});
4 changes: 2 additions & 2 deletions app/api/profile/popular-skills/route.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { NextRequest, NextResponse } from 'next/server';
import { Session } from 'next-auth';
import { getPopularSkills } from '@/server/services/profile/profile.service';
import { withAuth } from '@/lib/protectedRoute';


export const GET = withAuth(async (req: NextRequest, session: any) => {
export const GET = withAuth(async (req: NextRequest, _context: unknown, session: Session) => {
try {
const popularSkills = await getPopularSkills();
return NextResponse.json(popularSkills, {
Expand Down
3 changes: 2 additions & 1 deletion app/api/profile/reward-board/route.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Session } from 'next-auth';
import { withAuth } from "@/lib/protectedRoute";
import { getRewardBoard } from "@/server/services/rewardBoard";
import { NextResponse } from "next/server";

export const GET = withAuth(async (request, context, session) => {
export const GET = withAuth(async (request, _context: unknown, session: Session) => {
const { searchParams } = new URL(request.url);
const user_id = searchParams.get("user_id");
if (!user_id) {
Expand Down
12 changes: 6 additions & 6 deletions app/api/project/[project_id]/members/route.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { withAuth } from "@/lib/protectedRoute";
import { Session } from 'next-auth';
import { withAuth, RouteParams } from "@/lib/protectedRoute";
import { prisma } from "@/prisma/prisma";
import { isUserProjectMember } from "@/server/services/fileValidation";
import {
GetMembersByProjectId,
UpdateRoleMember,
} from "@/server/services/memberProject";

import { NextResponse } from "next/server";

export const GET = withAuth(async (request, context: any, session: any) => {
export const GET = withAuth<RouteParams<{ project_id: string }>>(async (request, { params }, session: Session) => {
try {
const { project_id } = await context.params;
const { project_id } = await params;
if (!project_id) {
return NextResponse.json(
{ error: "project_id is required" },
Expand Down Expand Up @@ -40,11 +40,11 @@ export const GET = withAuth(async (request, context: any, session: any) => {
}
});

export const PATCH = withAuth(async (request: Request, context: any, session: any) => {
export const PATCH = withAuth<RouteParams<{ project_id: string }>>(async (request: Request, { params }, session: Session) => {
try {
const body = await request.json();
const { member_id, role } = body;
const { project_id } = await context.params;
const { project_id } = await params;

console.log("body", member_id);
if (!member_id || !role) {
Expand Down
7 changes: 4 additions & 3 deletions app/api/project/[project_id]/members/status/route.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { withAuth } from "@/lib/protectedRoute";
import { Session } from 'next-auth';
import { withAuth, RouteParams } from "@/lib/protectedRoute";
import { UpdateStatusMember } from "@/server/services/memberProject";
import { isUserProjectMember } from "@/server/services/fileValidation";
import { NextResponse } from "next/server";

export const PATCH = withAuth(async (request: Request, context: any, session: any) => {
export const PATCH = withAuth<RouteParams<{ project_id: string }>>(async (request: Request, { params }, session: Session) => {
try {
const body = await request.json();
const { user_id, status, email, wasInOtherProject } = body;
const { project_id } = await context.params;
const { project_id } = await params;

if (!project_id) {
return NextResponse.json(
Expand Down
3 changes: 2 additions & 1 deletion app/api/project/check-invitation/route.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Session } from 'next-auth';
import { withAuth } from "@/lib/protectedRoute";
import { NextResponse } from "next/server";
import { CheckInvitation } from "@/server/services/projects";

export const GET = withAuth(async (request, context, session) => {
export const GET = withAuth(async (request, _context: unknown, session: Session) => {
const { searchParams } = new URL(request.url);
const invitationId = searchParams.get("invitation");
const user_id = searchParams.get("user_id");
Expand Down
5 changes: 3 additions & 2 deletions app/api/project/invite-member/route.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Session } from 'next-auth';
import { withAuth } from "@/lib/protectedRoute";
import { generateInvitation } from "@/server/services/inviteProjectMember";
import { isUserProjectMember } from "@/server/services/fileValidation";
import { NextResponse } from "next/server";

export const POST = withAuth(async (request, context, session) => {
export const POST = withAuth(async (request, _context: unknown, session: Session) => {
try {
const body = await request.json();

Expand All @@ -29,7 +30,7 @@ export const POST = withAuth(async (request, context, session) => {
const result = await generateInvitation(
body.hackathon_id,
session.user.id, // Use session user ID
session.user.name,
session.user?.name ?? "",
body.emails,
body.project_id, // Pass project_id if it exists
body.stage // Optional stage for Build Games invite links
Expand Down
7 changes: 4 additions & 3 deletions app/api/project/route.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { Session } from 'next-auth';
import { withAuth } from '@/lib/protectedRoute';
import { prisma } from '@/prisma/prisma';
import { GetProjectByHackathonAndUser } from '@/server/services/projects';
import { createProject } from '@/server/services/submitProject';
import { NextResponse } from 'next/server';
import { NextResponse } from 'next/server';

export const POST = withAuth(async (request,context ,session) => {
export const POST = withAuth(async (request, _context: unknown, session: Session) => {
try{
const body = await request.json();
const newProject = await createProject({ ...body, submittedBy: session.user.email });
Expand All @@ -25,7 +26,7 @@ export const POST = withAuth(async (request,context ,session) => {



export const GET = withAuth(async (request: Request, context, session) => {
export const GET = withAuth(async (request: Request, _context: unknown, session: Session) => {
try {
const { searchParams } = new URL(request.url);
const hackaton_id = searchParams.get("hackathon_id") ?? "";
Expand Down
32 changes: 21 additions & 11 deletions app/api/project/set-winner/route.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,43 @@
import { getAuthSession } from "@/lib/auth/authSession";
import { Session } from 'next-auth';
import { withAuthRole } from "@/lib/protectedRoute";
import { SetWinner } from "@/server/services/set-project-winner";
import { NextRequest, NextResponse } from "next/server";

export const PUT = withAuthRole("badge_admin", async (req: NextRequest) => {
export const PUT = withAuthRole("badge_admin", async (req: NextRequest, _context: unknown, session: Session) => {
const body = await req.json();
const session = await getAuthSession();
const name = session?.user.name || "user";
const name = session.user.name || "user";

try {
if (!body.project_id) {
return NextResponse.json(
{ error: "project_id parameter is required" },
{ success: false, error: "project_id parameter is required" },
{ status: 400 }
);
}
if (!body.isWinner) {
if (body.isWinner === undefined) {
return NextResponse.json(
{ error: "IsWinner parameter is required" },
{ success: false, error: "isWinner parameter is required" },
{ status: 400 }
);
}
const badge = await SetWinner(body.project_id, body.isWinner, name);

const result = await SetWinner(body.project_id, body.isWinner, name);

return NextResponse.json(badge, { status: 200 });
return NextResponse.json(result, { status: 200 });
} catch (error) {
console.error("Error checking user by email:", error);
console.error("Error setting project winner:", error);

// Handle known, safe errors that can be exposed to the client
if (error instanceof Error && error.message === "Project not found") {
return NextResponse.json(
{ success: false, error: "Project not found" },
{ status: 404 }
);
}

// For all other errors, return a generic message to avoid leaking internal details
return NextResponse.json(
{ error: "Internal server error" },
{ success: false, error: "Failed to update project winner status" },
{ status: 500 }
);
}
Expand Down
Loading