Implementation of dashboard UI and user workflow#1
Conversation
This workflow sets up Node.js CI to install dependencies, build the code, and run tests across multiple Node.js versions.
… new home landing page
…tion, and adding API routes and dependencies
…pdated the page files by adding instructor/participants folder
…rticipant session UI.
… make it more organize
…low and update OpenAI model to gpt-4o-mini
…ng issues among API routes and client components.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Pull request overview
Implements a new dashboard/UI experience and supporting server utilities/endpoints for participant + instructor workflows, including session management and Hume/OpenAI-based analysis.
Changes:
- Added participant and instructor dashboard pages/components plus shared UI utilities and styling updates.
- Introduced new Supabase SSR helpers and role-gated auth utilities (
requireParticipant/requireInstructor) and server auth actions (signUp/signIn/signOut). - Added multiple API routes for sessions, recordings, instructor student/assignment management, and Hume batch/token operations; added a Node.js CI workflow.
Reviewed changes
Copilot reviewed 69 out of 76 changed files in this pull request and generated 25 comments.
Show a summary per file
| File | Description |
|---|---|
| test-fetch.mjs | Adds a manual Hume API fetch script. |
| test-batch.ts | Adds a manual Hume batch SDK test script. |
| proxy.ts | Adds Supabase SSR cookie refresh logic intended for middleware. |
| package.json | Adds dependencies (Hume/OpenAI/Supabase SSR/etc.) and tweaks dev script. |
| lint_output.txt | Adds captured eslint output to the repo. |
| lib/utils/studentHelpers.ts | Adds student formatting + streak/status derivation helpers. |
| lib/supabaseServer.ts | Adds server-side Supabase SSR client factory with cookie handling. |
| lib/supabaseBrowser.ts | Adds browser-side Supabase SSR client factory. |
| lib/server/sessions/analyzeSession.ts | Adds OpenAI-powered session feedback generation + persistence. |
| lib/server/participant/getParticipantSessionDetails.ts | Adds participant session details fetch + transcript parsing. |
| lib/server/participant/getParticipantProfile.ts | Adds participant profile fetch with instructor name resolution. |
| lib/server/participant/getParticipantDashboard.ts | Adds participant dashboard data fetch (assignments/sessions/stats). |
| lib/server/instructor/getInstructorStudents.ts | Adds instructor student list fetch + derived stats (streak/avg score). |
| lib/server/instructor/getInstructorStudentDetails.ts | Adds instructor student details fetch + sessions/stats. |
| lib/server/instructor/getInstructorAssignments.ts | Adds instructor assignment history aggregation + filtering/stats. |
| lib/server/instructor/getInstructorAssignmentForEdit.ts | Adds instructor assignment fetch for editing plus participant list. |
| lib/server/auth/requireParticipant.ts | Adds participant role guard (redirects if not authenticated/authorized). |
| lib/server/auth/requireInstructor.ts | Adds instructor role guard (redirects if not authenticated/authorized). |
| components/ui.tsx | Adds basic shared UI primitives (TextInput/PrimaryButton/LinkText). |
| components/participantNavbar.tsx | Adds participant navigation (desktop/mobile) with profile integration. |
| components/StudentTable.tsx | Adds instructor dashboard student table UI. |
| components/StudentProgressPanel.tsx | Adds instructor dashboard student detail side panel UI. |
| components/StatMiniCard.tsx | Adds small stat display card UI. |
| components/ProfileMenu.tsx | Adds profile dropdown menu with sign-out action. |
| components/InstructorSidebar.tsx | Adds instructor sidebar + mobile slide-in navigation. |
| components/Header.tsx | Adds a header component with logo/user display. |
| components/GoalCard.tsx | Adds goal card UI for participant dashboard. |
| components/FeedbackCard.tsx | Adds feedback card UI. |
| components/DoorLoader.tsx | Adds animated loader component used for app loading state. |
| components/AuthCard.tsx | Adds auth form wrapper card component. |
| components/ActionCircle.tsx | Adds action “circle button” component used on dashboards. |
| app/register/register-form.tsx | Adds register form UI wired to signUp. |
| app/register/page.tsx | Adds register page with Suspense wrapper. |
| app/participant/sessions/page.tsx | Adds participant sessions list (assigned + completed) UI + data fetch. |
| app/participant/sessions/new/page.tsx | Adds participant new session entry route (client session UI). |
| app/participant/sessions/[sessionId]/page.tsx | Adds participant session details UI (feedback/transcript/instructor). |
| app/participant/profile/page.tsx | Adds participant profile page UI. |
| app/participant/dashboard/page.tsx | Adds participant dashboard page UI + data integration. |
| app/page.tsx | Replaces default Next landing page with OpenDoorsAI marketing landing page. |
| app/login/page.tsx | Adds login page with Suspense wrapper. |
| app/login/login-form.tsx | Adds login form UI wired to signIn. |
| app/loading.tsx | Adds global loading UI using DoorLoader. |
| app/layout.tsx | Updates site metadata title. |
| app/instructor/students/page.tsx | Adds instructor students list page UI + stats. |
| app/instructor/students/new/page.tsx | Adds instructor “create student account” page and form. |
| app/instructor/students/[studentId]/page.tsx | Adds instructor student detail page UI + session history display. |
| app/instructor/dashboard/page.tsx | Adds instructor dashboard page integrating students + assignments. |
| app/instructor/dashboard/DashboardClient.tsx | Adds client-side selection state for instructor dashboard. |
| app/instructor/assignments/page.tsx | Adds instructor assignments list page UI with filtering. |
| app/instructor/assignments/new/page.tsx | Adds instructor create-assignment page and form. |
| app/instructor/assignments/[assignmentId]/page.tsx | Adds instructor assignment edit page (duplicate route). |
| app/instructor/assignments/[assignmentId]/edit/page.tsx | Adds instructor assignment edit page (canonical edit route). |
| app/globals.css | Replaces default theme variables with brand theme tokens. |
| app/favicon.ico | Adds app favicon. |
| app/api/sessions/generate-prompt/route.ts | Adds OpenAI prompt generation endpoint for session setup. |
| app/api/sessions/create/route.ts | Expands session creation endpoint with auth + assignment linking. |
| app/api/sessions/complete/route.ts | Adds session completion endpoint that updates sessions/transcripts/assignments. |
| app/api/recordings/upload/route.ts | Adds recording upload endpoint to Supabase Storage + metadata persistence. |
| app/api/recordings/signed-url/route.ts | Adds signed recording URL generation endpoint. |
| app/api/instructor/students/route.ts | Adds instructor endpoint to create participant accounts. |
| app/api/instructor/students/[studentId]/route.ts | Adds instructor PATCH endpoint to update assigned student profiles. |
| app/api/instructor/assignments/route.ts | Adds instructor assignment creation endpoint. |
| app/api/instructor/assignments/[assignmentId]/route.ts | Adds instructor assignment update/delete endpoint (intent-based). |
| app/api/hume/token/route.ts | Adds endpoint to mint temporary Hume access tokens. |
| app/api/hume/batch/start/route.ts | Adds endpoint to start Hume batch analysis for a session recording. |
| app/api/hume/batch/poll/route.ts | Adds endpoint to poll Hume jobs and trigger OpenAI feedback generation. |
| app/api/health/route.ts | Improves Supabase health check endpoint comments/formatting. |
| app/actions/auth.ts | Adds server actions for sign up/in/out with role-based redirect. |
| .github/workflows/node.js.yml | Adds GitHub Actions Node.js CI workflow. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| export async function POST(req: Request) { | ||
| try { | ||
| // Extract request body | ||
| const { jobId, sessionId } = await req.json(); | ||
|
|
||
| // Validate required inputs | ||
| if (!jobId || !sessionId) { | ||
| return NextResponse.json( | ||
| { error: "jobId and sessionId required" }, | ||
| { status: 400 } | ||
| ); | ||
| } |
There was a problem hiding this comment.
This polling endpoint allows any caller to trigger analyzeSession(sessionId, ...) for an arbitrary session ID, without authentication/authorization. That’s both a data access issue and a cost-amplification vector (OpenAI calls). Require auth and verify ownership/permissions for sessionId before polling or running analysis.
|
|
||
| const formData = await req.formData(); | ||
| const intent = String(formData.get("_intent") || "").trim(); | ||
|
|
There was a problem hiding this comment.
If _intent is missing/invalid, this handler currently just redirects as if successful. That can mask client bugs and makes it harder to debug. Consider validating _intent and returning a 400 for unknown intents.
| if (intent !== "delete" && intent !== "update") { | |
| return NextResponse.json({ error: "Invalid intent." }, { status: 400 }); | |
| } |
| // Build dashboard rows for each student | ||
| const rows: StudentRow[] = students.map( | ||
| (student: { | ||
| user_id: string; | ||
| full_name: string | null; | ||
| created_at: string | null; | ||
| }) => { | ||
| // Sessions that belong to the current student | ||
| const studentSessions = sessionsData.filter( | ||
| (session) => session.participant_id === student.user_id | ||
| ); |
There was a problem hiding this comment.
Building rows currently does sessionsData.filter(...) for each student, which is O(students × sessions) and can become slow as data grows. Consider pre-grouping sessionsData into a Map keyed by participant_id, then doing O(1) lookups per student to compute streak/avg stats.
|
|
||
| > opendoorsai@0.1.0 lint | ||
| > eslint | ||
|
|
||
|
|
||
| /Users/nirathhussan/GitHub/OpenDoorsAI/app/api/hume/batch/poll/route.ts | ||
| 57:39 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any | ||
| 112:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any | ||
|
|
||
| /Users/nirathhussan/GitHub/OpenDoorsAI/app/api/hume/batch/start/route.ts | ||
| 96:19 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any | ||
|
|
||
| /Users/nirathhussan/GitHub/OpenDoorsAI/app/api/instructor/assignments/[assignmentId]/route.ts | ||
| 5:54 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any | ||
|
|
||
| /Users/nirathhussan/GitHub/OpenDoorsAI/app/api/recordings/upload/route.ts | ||
| 146:17 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any | ||
|
|
||
| /Users/nirathhussan/GitHub/OpenDoorsAI/app/api/sessions/complete/route.ts | ||
| 3:10 warning 'analyzeSession' is defined but never used @typescript-eslint/no-unused-vars | ||
|
|
||
| /Users/nirathhussan/GitHub/OpenDoorsAI/app/api/sessions/generate-prompt/route.ts | ||
| 30:40 warning 'assignmentError' is assigned a value but never used @typescript-eslint/no-unused-vars | ||
| 114:40 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any | ||
|
|
||
| /Users/nirathhussan/GitHub/OpenDoorsAI/app/participant/dashboard/page.tsx | ||
| 9:3 warning 'Eye' is defined but never used @typescript-eslint/no-unused-vars | ||
|
|
||
| /Users/nirathhussan/GitHub/OpenDoorsAI/components/Header.tsx | ||
| 7:9 warning Using `<img>` could result in slower LCP and higher bandwidth. Consider using `<Image />` from `next/image` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element @next/next/no-img-element | ||
|
|
||
| /Users/nirathhussan/GitHub/OpenDoorsAI/lib/server/instructor/getInstructorStudents.ts | ||
| 46:21 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any | ||
|
|
||
| /Users/nirathhussan/GitHub/OpenDoorsAI/proxy.ts | ||
| 6:7 error 'res' is never reassigned. Use 'const' instead prefer-const | ||
|
|
||
| /Users/nirathhussan/GitHub/OpenDoorsAI/test-fetch.mjs | ||
| 1:8 warning 'fs' is defined but never used @typescript-eslint/no-unused-vars | ||
|
|
||
| ✖ 13 problems (8 errors, 5 warnings) |
There was a problem hiding this comment.
This file appears to be a local eslint run output (includes absolute paths like /Users/...) and doesn’t belong in the repository/PR. It will quickly go stale and can leak developer environment details. Consider removing it from source control and relying on CI logs for lint output instead.
| const res = NextResponse.next({ request: req }); | ||
|
|
There was a problem hiding this comment.
res is never reassigned, but it’s declared with let. This currently fails eslint (prefer-const) as shown in lint_output. Change it to const res = NextResponse.next(...).
| function StatusBadge({ status }: { status: string }) { | ||
| const styles = | ||
| status === "Active" | ||
| ? "bg-emerald-100 text-emerald-700" | ||
| : status === "Needs Help" | ||
| ? "bg-amber-100 text-amber-700" | ||
| : "bg-gray-100 text-gray-500"; |
There was a problem hiding this comment.
StatusBadge checks for "Needs Help", but the derived statuses produced elsewhere in this PR use "Needs Attention" (see DerivedStudentStatus). As a result, “Needs Attention” will fall into the default styling. Align the label enum used across the dashboard (either update the check here or standardize the status strings).
| <Link | ||
| href={`/participant/sessions/${session.id}`} | ||
| className="inline-flex items-center gap-2 rounded-xl border-2 border-brand-muted bg-white px-4 py-2 text-sm font-semibold text-gray-800 hover:border-brand-primary transition-colors" | ||
| > | ||
| View Session | ||
| </Link> |
There was a problem hiding this comment.
This instructor page links to /participant/sessions/${session.id}, but that route is protected by requireParticipant() and will redirect instructors to /login. Provide an instructor-accessible session details route (e.g., /instructor/sessions/[id]) or a shared details page that authorizes both roles.
| cache: 'npm' | ||
| - run: npm ci | ||
| - run: npm run build --if-present | ||
| - run: npm test |
There was a problem hiding this comment.
The workflow runs npm test, but this repo’s package.json doesn’t define a test script. On GitHub Actions this will fail with “Missing script: test”. Update the workflow to run an existing script (e.g., npm run lint) or change this step to npm test --if-present / npm run test --if-present.
| - run: npm test | |
| - run: npm test --if-present |
| export async function GET(req: Request) { | ||
| /** | ||
| * Step 1: Extract session_id from query params | ||
| * Example: /api/... ?session_id=123 | ||
| */ | ||
| const { searchParams } = new URL(req.url); | ||
| const session_id = searchParams.get("session_id"); | ||
|
|
||
| // Validate input | ||
| if (!session_id) { | ||
| return NextResponse.json( | ||
| { ok: false, error: "session_id required" }, | ||
| { status: 400 } | ||
| ); | ||
| } |
There was a problem hiding this comment.
This route returns a signed URL for any session_id without authenticating/authorizing the caller. Because it uses the Supabase service role client, this can expose private recordings to anyone who can guess a session ID. Require an authenticated user (participant owner or assigned instructor) and verify session ownership before generating the signed URL.
| export async function POST(req: NextRequest) { | ||
| try { | ||
| const body = await req.json(); | ||
|
|
||
| const { | ||
| sessionId, | ||
| compactTranscript, | ||
| durationSeconds, | ||
| transcriptStatus, | ||
| }: { | ||
| sessionId?: string; | ||
| compactTranscript?: string; | ||
| durationSeconds?: number; | ||
| transcriptStatus?: string; | ||
| } = body; | ||
|
|
||
| // ============================== | ||
| // 1. VALIDATION | ||
| // ============================== | ||
| if (!sessionId) { | ||
| return NextResponse.json( | ||
| { error: "sessionId is required" }, | ||
| { status: 400 } | ||
| ); | ||
| } |
There was a problem hiding this comment.
This completion endpoint uses the service-role Supabase client and performs updates purely based on a sessionId from the request body, with no authentication/authorization. That allows anyone to mark any session as completed and write transcripts. Authenticate the caller and verify they own the session (or are an authorized instructor) before updating session/transcript/assignment records.
dd6c45a to
e98b30d
Compare
…ved some unecessary stuff on some pages
This pull request introduces several new API endpoints and authentication actions to support instructor and participant workflows, Hume AI emotion analysis integration, and robust session management. The changes include new authentication utilities, instructor assignment and student management endpoints, and endpoints for starting and polling Hume AI batch jobs as well as securely generating Hume access tokens. Additionally, a Node.js CI workflow is added for automated testing and builds.
Authentication & User Management:
signUp,signIn, andsignOutserver actions inapp/actions/auth.tsto handle user registration, login, and logout, including role-based dashboard redirects and error handling.PATCHendpoint inapp/api/instructor/students/[studentId]/route.tsfor instructors to update their students’ profiles, with validation to ensure only assigned students can be updated. (app/api/instructor/students/[studentId]/route.tsR1-R128)Instructor Assignment Management:
POSTendpoint inapp/api/instructor/assignments/route.tsfor instructors to create new assignments for participants, including validation and error handling.POSTendpoint inapp/api/instructor/assignments/[assignmentId]/route.tsfor updating or deleting assignments, with intent-based branching and instructor authorization. (app/api/instructor/assignments/[assignmentId]/route.tsR1-R65)Hume AI Integration:
POSTendpoint inapp/api/hume/batch/start/route.tsto start a Hume AI batch inference job for a session recording, including generating a signed URL for the recording and returning the Hume job ID.POSTendpoint inapp/api/hume/batch/poll/route.tsto poll the status of a Hume AI job, extract and aggregate emotion data upon completion, and trigger feedback analysis with emotional context.GETendpoint inapp/api/hume/token/route.tsto securely generate and return a temporary Hume access token for frontend use, never exposing secret keys.Infrastructure & Miscellaneous:
.github/workflows/node.js.ymlto automate dependency installation, build, and test processes for multiple Node versions.app/api/health/route.tswith improved comments and error handling.