Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
fac67cf
Implement initial dashboard UI with new components, branding, and sty…
NirathH Mar 2, 2026
7b87f8c
Add Node.js CI workflow for testing and building
NirathH Mar 2, 2026
72de113
Simple api tests
Edison222 Mar 3, 2026
252386d
Merge remote-tracking branch 'origin/mvp' into edison-test-br
Edison222 Mar 3, 2026
e7da1a7
Implemented user auth + added navbar on dashboard and small updates +…
Edison222 Mar 3, 2026
df1e4f8
Added a session page
Edison222 Mar 3, 2026
0e53111
feat: Hume AI integration, implement session client for voice interac…
NirathH Mar 3, 2026
de4fb48
Added Instructor page + Updated registration with a role question + U…
Edison222 Mar 6, 2026
59e2d53
Add session architecture with transcript storage, assignments, and pa…
Edison222 Mar 14, 2026
e9531c0
Quick deployment fix
Edison222 Mar 14, 2026
395f25f
Quick deployment fix
Edison222 Mar 14, 2026
74ad51a
Quick deployment fix
Edison222 Mar 14, 2026
b44f05f
Route url/userid parms Fix
Edison222 Mar 14, 2026
1a8fa05
Participants Session Feedback implemented
Edison222 Mar 24, 2026
a16102f
Participants Session Feedback implemented
Edison222 Mar 24, 2026
952c156
Added Students page for instructors
Edison222 Mar 24, 2026
92f1218
Instrucors can now create students account/Fixed backend structure to…
Edison222 Mar 31, 2026
a97c4d3
feat: integrate Hume batch emotion analysis into session completion f…
NirathH Mar 31, 2026
0e754ff
feat(instructor): add assignments flow and connect dashboard to database
Edison222 Apr 15, 2026
a1ff0a3
refactor: AI prompting for assigned sessions
NirathH Apr 15, 2026
a0acd25
feat: improve type safety, optimize session management, and fix linti…
NirathH Apr 16, 2026
99d8203
Added a Door loader display on login and made UI updates
Edison222 Apr 20, 2026
db9cc98
UI updates
Edison222 Apr 20, 2026
289aa12
feat: redesign instructor dashboard with metrics and interactive stud…
NirathH Apr 22, 2026
e98b30d
feat: implement instructor session review portal and add API authenti…
NirathH Apr 22, 2026
beb4949
UI update on the student page on the instructor side
Edison222 Apr 22, 2026
7cb0510
UI update on the student page on the instructor side
Edison222 Apr 22, 2026
e12656a
UI update on the student page on the instructor side
Edison222 Apr 22, 2026
1af0e4b
UI update on the student page on the instructor side
Edison222 Apr 22, 2026
f824114
Session feedback fix
Edison222 Apr 22, 2026
c815f90
Added dashboards and worked on UI samll upgrades
Edison222 Apr 23, 2026
4081945
Fixed Dashboards queries and Feedback page/results
Edison222 Apr 28, 2026
952008b
Fixed an session title issue and added a new profile page for instruc…
Edison222 Apr 28, 2026
7484e67
Fixed an session title issue and added a new profile page for instruc…
Edison222 Apr 29, 2026
40b25e0
Added mediaPipe video analysis and worked on some UI changes
Edison222 Apr 30, 2026
caf774b
Added a weekly feedback feature
Edison222 May 1, 2026
13fc25a
Small participant page navbar update
Edison222 May 3, 2026
6e61b74
Tailored the session analysis prompt, so it be more detailed and remo…
Edison222 May 3, 2026
e828b29
feat: font size increased
NirathH May 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
25 changes: 25 additions & 0 deletions .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Node.js CI

on:
push:
branches: ["main"]
pull_request:
branches: ["main"]

jobs:
build:
runs-on: ubuntu-latest
env:
NEXT_PUBLIC_SUPABASE_URL: ${{ secrets.NEXT_PUBLIC_SUPABASE_URL }}
NEXT_PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.NEXT_PUBLIC_SUPABASE_ANON_KEY }}
SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }}

steps:
- uses: actions/checkout@v4
- name: Use Node.js 20
uses: actions/setup-node@v4
with:
node-version: "20.x"
cache: "npm"
- run: npm ci
- run: npm run build --if-present
242 changes: 242 additions & 0 deletions app/actions/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
"use server";

import { redirect } from "next/navigation";
import { createSupabaseServer } from "@/lib/supabaseServer";

function encodeMessage(message: string): string {
return encodeURIComponent(message);
}

export async function signUp(formData: FormData): Promise<void> {
const email = String(formData.get("email") || "").trim();
const password = String(formData.get("password") || "");
const fullName = String(formData.get("full_name") || "").trim();
const role = String(formData.get("role") || "participant");

if (!email || !password || !fullName) {
redirect(`/register?error=${encodeMessage("All fields are required.")}`);
}

const supabase = await createSupabaseServer();

const { error } = await supabase.auth.signUp({
email,
password,
options: {
data: {
full_name: fullName,
role,
},
},
});

if (error) {
redirect(`/register?error=${encodeMessage(error.message)}`);
}

if (role === "instructor") {
redirect("/instructor/dashboard");
}

redirect("/participant/dashboard");
}

export async function signIn(formData: FormData): Promise<void> {
const email = String(formData.get("email") || "").trim();
const password = String(formData.get("password") || "");

if (!email || !password) {
redirect(
`/login?error=${encodeMessage("Email and password are required.")}`
);
}

const supabase = await createSupabaseServer();

const { error } = await supabase.auth.signInWithPassword({
email,
password,
});

if (error) {
redirect(`/login?error=${encodeMessage(error.message)}`);
}

const {
data: { user },
error: userError,
} = await supabase.auth.getUser();

if (userError || !user) {
redirect(
`/login?error=${encodeMessage("Could not get logged in user.")}`
);
}

const { data: profile, error: profileError } = await supabase
.from("profiles")
.select("role")
.eq("user_id", user.id)
.single();

if (profileError || !profile) {
redirect(
`/login?error=${encodeMessage("Could not load user profile.")}`
);
}

if (profile.role === "instructor") {
redirect("/instructor/dashboard");
}

redirect("/participant/dashboard");
}

export async function signOut(): Promise<void> {
const supabase = await createSupabaseServer();
await supabase.auth.signOut();
redirect("/login");
}

export async function updatePassword(formData: FormData): Promise<void> {
const currentPassword = String(formData.get("currentPassword") || "");
const newPassword = String(formData.get("newPassword") || "");
const confirmPassword = String(formData.get("confirmPassword") || "");

if (!currentPassword || !newPassword || !confirmPassword) {
redirect(
`/participant/profile?error=${encodeMessage(
"All password fields are required."
)}`
);
}

if (newPassword !== confirmPassword) {
redirect(
`/participant/profile?error=${encodeMessage(
"New passwords do not match."
)}`
);
}

if (newPassword.length < 6) {
redirect(
`/participant/profile?error=${encodeMessage(
"New password must be at least 6 characters."
)}`
);
}

const supabase = await createSupabaseServer();

const {
data: { user },
error: userError,
} = await supabase.auth.getUser();

if (userError || !user || !user.email) {
redirect(
`/participant/profile?error=${encodeMessage(
"Could not load current user."
)}`
);
}

const { error: verifyError } = await supabase.auth.signInWithPassword({
email: user.email,
password: currentPassword,
});

if (verifyError) {
redirect(
`/participant/profile?error=${encodeMessage(
"Current password is incorrect."
)}`
);
}

const { error: updateError } = await supabase.auth.updateUser({
password: newPassword,
});

if (updateError) {
redirect(
`/participant/profile?error=${encodeMessage(updateError.message)}`
);
}

redirect(
`/participant/profile?success=${encodeMessage(
"Password updated successfully."
)}`
);
}
export async function updateInstructorPassword(
formData: FormData
): Promise<void> {
const currentPassword = String(formData.get("currentPassword") || "");
const newPassword = String(formData.get("newPassword") || "");
const confirmPassword = String(formData.get("confirmPassword") || "");

const redirectPath = "/instructor/profile";

if (!currentPassword || !newPassword || !confirmPassword) {
redirect(
`${redirectPath}?error=${encodeMessage(
"All password fields are required."
)}`
);
}

if (newPassword !== confirmPassword) {
redirect(
`${redirectPath}?error=${encodeMessage("New passwords do not match.")}`
);
}

if (newPassword.length < 6) {
redirect(
`${redirectPath}?error=${encodeMessage(
"New password must be at least 6 characters."
)}`
);
}

const supabase = await createSupabaseServer();

const {
data: { user },
error: userError,
} = await supabase.auth.getUser();

if (userError || !user || !user.email) {
redirect(
`${redirectPath}?error=${encodeMessage("Could not load current user.")}`
);
}

const { error: verifyError } = await supabase.auth.signInWithPassword({
email: user.email,
password: currentPassword,
});

if (verifyError) {
redirect(
`${redirectPath}?error=${encodeMessage("Current password is incorrect.")}`
);
}

const { error: updateError } = await supabase.auth.updateUser({
password: newPassword,
});

if (updateError) {
redirect(`${redirectPath}?error=${encodeMessage(updateError.message)}`);
}

redirect(
`${redirectPath}?success=${encodeMessage(
"Password updated successfully."
)}`
);
}
27 changes: 25 additions & 2 deletions app/api/health/route.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,40 @@
import { NextResponse } from "next/server";
import { supabaseAdmin } from "@/lib/supabaseAdmin";

/**
* Forces this route to run on Node.js (not edge runtime).
* Needed because Supabase Admin client requires Node environment.
*/
export const runtime = "nodejs";

/**
* GET /api/...
*
* Purpose:
* - Simple health check for Supabase connection
* - Verifies we can query the "sessions" table
*/
export async function GET() {
// Try to fetch 1 session record (just to test DB access)
const { data, error } = await supabaseAdmin
.from("sessions")
.select("id")
.limit(1);

// If query fails → return error response
if (error) {
return NextResponse.json({ ok: false, error: error.message }, { status: 500 });
return NextResponse.json(
{
ok: false,
error: error.message,
},
{ status: 500 }
);
}

return NextResponse.json({ ok: true, sample: data ?? [] });
// If successful → return sample data
return NextResponse.json({
ok: true,
sample: data ?? [],
});
}
Loading
Loading