From dab3d81afb8758867788b6f90f1aee54c875c915 Mon Sep 17 00:00:00 2001
From: Chenxin Yan
Date: Wed, 15 Oct 2025 02:32:34 -0400
Subject: [PATCH 01/39] fix(server): update userCourses Schema
---
packages/server/convex/schemas/courses.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/server/convex/schemas/courses.ts b/packages/server/convex/schemas/courses.ts
index 33b739f7..1df38627 100644
--- a/packages/server/convex/schemas/courses.ts
+++ b/packages/server/convex/schemas/courses.ts
@@ -31,7 +31,7 @@ const prerequisites = v.union(
const userCourses = {
userId: v.string(),
- courseId: v.id("courses"),
+ courseCode: v.string(), // CSCI-UA 101
title: v.string(),
year: v.number(), // 2025
term: v.union(
From 31a427e3e9f1ef5d7acf8c949c91c94b64a5c809 Mon Sep 17 00:00:00 2001
From: Emily Silkina
Date: Mon, 3 Nov 2025 11:45:07 -0500
Subject: [PATCH 02/39] Created account page
---
apps/web/src/app/dashboard/account/page.tsx | 49 +++++++++++++++++++
.../components/event-calendar/agenda-view.tsx | 0
2 files changed, 49 insertions(+)
create mode 100644 apps/web/src/app/dashboard/account/page.tsx
create mode 100644 apps/web/src/components/event-calendar/agenda-view.tsx
diff --git a/apps/web/src/app/dashboard/account/page.tsx b/apps/web/src/app/dashboard/account/page.tsx
new file mode 100644
index 00000000..629c89cf
--- /dev/null
+++ b/apps/web/src/app/dashboard/account/page.tsx
@@ -0,0 +1,49 @@
+"use client";
+
+import { useUser } from "@clerk/nextjs";
+import { useQuery, useMutation } from "convex/react";
+import { api } from "@albert-plus/server/convex/_generated/api";
+
+export default function ProfilePage() {
+ const { user, isLoaded } = useUser();
+ const student = useQuery(api.students.getCurrentStudent);
+ const upsert = useMutation(api.students.upsertCurrentStudent);
+
+ if (!isLoaded) return Loading...
;
+
+ return (
+
+
Profile
+
+
+ Name: {user?.fullName || "Unknown User"}
+
+
+ Email: {user?.primaryEmailAddress?.emailAddress || ""}
+
+
+ {student && (
+ <>
+
+ School: {student.school}
+
+
+ Programs:{" "}
+ {student.programs?.length > 0
+ ? student.programs.join(", ")
+ : "None"}
+
+ >
+ )}
+
+ {!student && (
+ <>
+
+ HIIII
+
+
+ >
+ )}
+
+ );
+}
diff --git a/apps/web/src/components/event-calendar/agenda-view.tsx b/apps/web/src/components/event-calendar/agenda-view.tsx
new file mode 100644
index 00000000..e69de29b
From 2cca915a6d4d90b74c53e1025dfbd741fce091e0 Mon Sep 17 00:00:00 2001
From: Chenxin Yan
Date: Wed, 5 Nov 2025 16:31:35 -0500
Subject: [PATCH 03/39] add account header
---
apps/web/src/app/dashboard/@header/account/page.tsx | 5 +++++
1 file changed, 5 insertions(+)
create mode 100644 apps/web/src/app/dashboard/@header/account/page.tsx
diff --git a/apps/web/src/app/dashboard/@header/account/page.tsx b/apps/web/src/app/dashboard/@header/account/page.tsx
new file mode 100644
index 00000000..e3c55fa5
--- /dev/null
+++ b/apps/web/src/app/dashboard/@header/account/page.tsx
@@ -0,0 +1,5 @@
+import { AppHeader } from "@/app/dashboard/components/app-header";
+
+export default function AdminHeader() {
+ return ;
+}
From eaff8d23994e934a78f0a31bea5447e8c81a9bc6 Mon Sep 17 00:00:00 2001
From: Chenxin Yan
Date: Wed, 5 Nov 2025 16:43:08 -0500
Subject: [PATCH 04/39] add table joint for get current student
---
packages/server/convex/students.ts | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/packages/server/convex/students.ts b/packages/server/convex/students.ts
index 2077cc88..1a2e0b7e 100644
--- a/packages/server/convex/students.ts
+++ b/packages/server/convex/students.ts
@@ -1,14 +1,23 @@
import { omit } from "convex-helpers";
+import { getAll } from "convex-helpers/server/relationships";
import { protectedMutation, protectedQuery } from "./helpers/auth";
import { students } from "./schemas/students";
export const getCurrentStudent = protectedQuery({
args: {},
handler: async (ctx) => {
- return await ctx.db
+ const student = await ctx.db
.query("students")
.withIndex("by_user_id", (q) => q.eq("userId", ctx.user.subject))
.unique();
+ if (!student) return null;
+
+ const programs = await getAll(ctx.db, student.programs);
+
+ return {
+ ...student,
+ programs,
+ };
},
});
From 659d64c925384194afb06f6826edd23b8a0f2a46 Mon Sep 17 00:00:00 2001
From: Chenxin Yan
Date: Wed, 5 Nov 2025 17:16:46 -0500
Subject: [PATCH 05/39] update query
---
apps/web/src/app/dashboard/account/page.tsx | 25 +++++++++------------
1 file changed, 11 insertions(+), 14 deletions(-)
diff --git a/apps/web/src/app/dashboard/account/page.tsx b/apps/web/src/app/dashboard/account/page.tsx
index 629c89cf..02f87f4b 100644
--- a/apps/web/src/app/dashboard/account/page.tsx
+++ b/apps/web/src/app/dashboard/account/page.tsx
@@ -1,15 +1,19 @@
"use client";
-import { useUser } from "@clerk/nextjs";
-import { useQuery, useMutation } from "convex/react";
import { api } from "@albert-plus/server/convex/_generated/api";
+import { useUser } from "@clerk/nextjs";
+import { useConvexAuth, useMutation, useQuery } from "convex/react";
export default function ProfilePage() {
- const { user, isLoaded } = useUser();
- const student = useQuery(api.students.getCurrentStudent);
- const upsert = useMutation(api.students.upsertCurrentStudent);
+ const { isAuthenticated } = useConvexAuth();
+ const { user } = useUser();
- if (!isLoaded) return Loading...
;
+ const student = useQuery(
+ api.students.getCurrentStudent,
+ isAuthenticated ? {} : "skip",
+ );
+
+ const upsert = useMutation(api.students.upsertCurrentStudent);
return (
@@ -36,14 +40,7 @@ export default function ProfilePage() {
>
)}
- {!student && (
- <>
-
- HIIII
-
-
- >
- )}
+ {!student &&
HIIII
}
);
}
From 6c5ee7b8ce92bf0d64df88542d61980598543765 Mon Sep 17 00:00:00 2001
From: Emily Silkina
Date: Tue, 11 Nov 2025 20:31:58 -0500
Subject: [PATCH 06/39] Fixed program name
---
apps/web/src/app/dashboard/account/page.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/apps/web/src/app/dashboard/account/page.tsx b/apps/web/src/app/dashboard/account/page.tsx
index 02f87f4b..edadabe4 100644
--- a/apps/web/src/app/dashboard/account/page.tsx
+++ b/apps/web/src/app/dashboard/account/page.tsx
@@ -34,7 +34,7 @@ export default function ProfilePage() {
Programs:{" "}
{student.programs?.length > 0
- ? student.programs.join(", ")
+ ? student.programs.map(p => p.name).join(", ")
: "None"}
>
From 6064be0c1929dbe81ad3651e22ef7963c517118c Mon Sep 17 00:00:00 2001
From: Chenxin Yan
Date: Wed, 12 Nov 2025 22:01:45 -0500
Subject: [PATCH 07/39] lint
---
apps/web/src/app/dashboard/account/page.tsx | 2 +-
packages/server/convex/students.ts | 1 -
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/apps/web/src/app/dashboard/account/page.tsx b/apps/web/src/app/dashboard/account/page.tsx
index edadabe4..6fda13a3 100644
--- a/apps/web/src/app/dashboard/account/page.tsx
+++ b/apps/web/src/app/dashboard/account/page.tsx
@@ -34,7 +34,7 @@ export default function ProfilePage() {
Programs:{" "}
{student.programs?.length > 0
- ? student.programs.map(p => p.name).join(", ")
+ ? student.programs.map((p) => p.name).join(", ")
: "None"}
>
diff --git a/packages/server/convex/students.ts b/packages/server/convex/students.ts
index 74bcaf2f..cc9003c6 100644
--- a/packages/server/convex/students.ts
+++ b/packages/server/convex/students.ts
@@ -1,7 +1,6 @@
import { ConvexError } from "convex/values";
import { omit } from "convex-helpers";
-import { getAll } from "convex-helpers/server/relationships";
import { getOneFrom } from "convex-helpers/server/relationships";
import { partial } from "convex-helpers/validators";
import { protectedMutation, protectedQuery } from "./helpers/auth";
From 8bccc6337c3423d43214d1e1ad3ff7db202243bc Mon Sep 17 00:00:00 2001
From: Chenxin Yan
Date: Wed, 12 Nov 2025 22:31:40 -0500
Subject: [PATCH 08/39] adadd program table joints to get student
---
packages/server/convex/students.ts | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/packages/server/convex/students.ts b/packages/server/convex/students.ts
index cc9003c6..3bee6842 100644
--- a/packages/server/convex/students.ts
+++ b/packages/server/convex/students.ts
@@ -1,5 +1,5 @@
import { ConvexError } from "convex/values";
-import { omit } from "convex-helpers";
+import { asyncMap, omit } from "convex-helpers";
import { getOneFrom } from "convex-helpers/server/relationships";
import { partial } from "convex-helpers/validators";
@@ -26,9 +26,16 @@ export const getCurrentStudent = protectedQuery({
"_id",
);
+ const programs = await asyncMap(student.programs, async (programId) => {
+ return await ctx.db.get(programId);
+ });
+
+ const validPrograms = programs.filter((p) => p !== null);
+
return {
...student,
school,
+ programs: validPrograms,
};
},
});
From 4ba8f578bd0cf1ce7a9c140c421ce757af89207e Mon Sep 17 00:00:00 2001
From: Emily Silkina
Date: Thu, 13 Nov 2025 11:21:06 -0500
Subject: [PATCH 09/39] Added edit profile component
---
apps/web/src/app/dashboard/account/page.tsx | 78 ++++++++++++++++++---
apps/web/tsconfig.json | 14 +++-
2 files changed, 80 insertions(+), 12 deletions(-)
diff --git a/apps/web/src/app/dashboard/account/page.tsx b/apps/web/src/app/dashboard/account/page.tsx
index 6fda13a3..b5c59c43 100644
--- a/apps/web/src/app/dashboard/account/page.tsx
+++ b/apps/web/src/app/dashboard/account/page.tsx
@@ -3,6 +3,19 @@
import { api } from "@albert-plus/server/convex/_generated/api";
import { useUser } from "@clerk/nextjs";
import { useConvexAuth, useMutation, useQuery } from "convex/react";
+import {
+ Dialog,
+ DialogClose,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "@/components/ui/dialog"
+import { Button } from "@/components/ui/button";
+import { Input } from "@/components/ui/input";
+import { Label } from "@/components/ui/label";
export default function ProfilePage() {
const { isAuthenticated } = useConvexAuth();
@@ -15,6 +28,15 @@ export default function ProfilePage() {
const upsert = useMutation(api.students.upsertCurrentStudent);
+ if (student) {
+ console.log("STUDENTTT");
+ console.log(student.programs);
+ } else{
+ console.log("STUDENT NULL");
+ }
+
+
+
return (
Profile
@@ -29,18 +51,56 @@ export default function ProfilePage() {
{student && (
<>
- School: {student.school}
-
-
- Programs:{" "}
- {student.programs?.length > 0
- ? student.programs.map((p) => p.name).join(", ")
- : "None"}
-
+
School:{" "}
+ {student.school
+ ? `${student.school.name} (${
+ student.school.level
+ ? student.school.level.charAt(0).toUpperCase() + student.school.level.slice(1).toLowerCase()
+ : ""
+ })`
+ : "N/A"}
+
+
+ Programs:{" "}
+ {student.programs?.length > 0
+ ? student.programs.map((p) => p.name).join(", ")
+ : "None"}
+
>
)}
- {!student &&
HIIII
}
+
);
}
diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json
index 19c51c83..b575f7da 100644
--- a/apps/web/tsconfig.json
+++ b/apps/web/tsconfig.json
@@ -1,7 +1,11 @@
{
"compilerOptions": {
"target": "ES2017",
- "lib": ["dom", "dom.iterable", "esnext"],
+ "lib": [
+ "dom",
+ "dom.iterable",
+ "esnext"
+ ],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
@@ -19,7 +23,9 @@
}
],
"paths": {
- "@/*": ["./src/*"]
+ "@/*": [
+ "./src/*"
+ ]
}
},
"include": [
@@ -29,5 +35,7 @@
".next/types/**/*.ts",
".next/dev/types/**/*.ts"
],
- "exclude": ["node_modules"]
+ "exclude": [
+ "node_modules"
+ ]
}
From b0503f5b9e2d15e2fd644646455e2cdab6a06907 Mon Sep 17 00:00:00 2001
From: Emily Silkina
Date: Thu, 13 Nov 2025 12:59:27 -0500
Subject: [PATCH 10/39] Added form to popup
---
.../account/components/editProfile.tsx | 554 ++++++++++++++++++
apps/web/src/app/dashboard/account/page.tsx | 53 +-
2 files changed, 556 insertions(+), 51 deletions(-)
create mode 100644 apps/web/src/app/dashboard/account/components/editProfile.tsx
diff --git a/apps/web/src/app/dashboard/account/components/editProfile.tsx b/apps/web/src/app/dashboard/account/components/editProfile.tsx
new file mode 100644
index 00000000..b8273edf
--- /dev/null
+++ b/apps/web/src/app/dashboard/account/components/editProfile.tsx
@@ -0,0 +1,554 @@
+"use client";
+
+import { api } from "@albert-plus/server/convex/_generated/api";
+import type { Doc, Id } from "@albert-plus/server/convex/_generated/dataModel";
+import { useForm } from "@tanstack/react-form";
+import {
+ useConvexAuth,
+ useMutation,
+ usePaginatedQuery,
+ useQuery,
+} from "convex/react";
+import type { FunctionArgs } from "convex/server";
+import { useRouter } from "next/navigation";
+import React, { Activity } from "react";
+import { toast } from "sonner";
+import z from "zod";
+import MultipleSelector from "@/app/onboarding/component/multiselect";
+import { SchoolCombobox } from "@/app/onboarding/component/school-combobox";
+import { Badge } from "@/components/ui/badge";
+import { Button } from "@/components/ui/button";
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/components/ui/card";
+import {
+ FieldContent,
+ FieldError,
+ FieldGroup,
+ FieldLabel,
+ Field as UIField,
+} from "@/components/ui/field";
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/components/ui/select";
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipTrigger,
+} from "@/components/ui/tooltip";
+import DegreeProgreeUpload from "@/modules/report-parsing/components/degree-progress-upload";
+import type { UserCourse } from "@/modules/report-parsing/types";
+import type { StartingTerm } from "@/modules/report-parsing/utils/parse-starting-term";
+import { getTermAfterSemesters, type Term } from "@/utils/term";
+import {
+ Dialog,
+ DialogClose,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "@/components/ui/dialog"
+
+const dateSchema = z.object({
+ year: z.number().int().min(2000).max(2100),
+ term: z.union([
+ z.literal("spring"),
+ z.literal("fall"),
+ z.literal("j-term"),
+ z.literal("summer"),
+ ]),
+});
+
+const onboardingFormSchema = z
+ .object({
+ school: z.string({
+ error: (issue) =>
+ issue.input === undefined ? "Please select a school" : "Invalid input",
+ }),
+ programs: z.array(z.string()).min(1, "At least one program is required"),
+ startingDate: dateSchema,
+ expectedGraduationDate: dateSchema,
+ // User courses
+ userCourses: z.array(z.object()).optional(),
+ })
+ .refine(
+ (data) => {
+ const startYear = data.startingDate.year;
+ const startTerm = data.startingDate.term;
+ const endYear = data.expectedGraduationDate.year;
+ const endTerm = data.expectedGraduationDate.term;
+
+ // Convert to comparable numbers (spring=0, fall=1)
+ const startValue = startYear * 2 + (startTerm === "fall" ? 1 : 0);
+ const endValue = endYear * 2 + (endTerm === "fall" ? 1 : 0);
+
+ return endValue > startValue;
+ },
+ {
+ message: "Expected graduation date must be after starting date",
+ path: ["expectedGraduationDate"],
+ },
+ );
+
+export function EditProfilePopup() {
+ const router = useRouter();
+ const { isAuthenticated } = useConvexAuth();
+ const [isFileLoaded, setIsFileLoaded] = React.useState(false);
+ const [currentStep, setCurrentStep] = React.useState<1 | 2>(1);
+
+ // actions
+ const upsertStudent = useMutation(api.students.upsertCurrentStudent);
+ const importUserCourses = useMutation(api.userCourses.importUserCourses);
+
+ // schools
+ const schools = useQuery(
+ api.schools.getSchools,
+ isAuthenticated ? {} : ("skip" as const),
+ );
+
+ // Programs
+ const [programsQuery, setProgramsQuery] = React.useState(
+ undefined,
+ );
+ const {
+ results: programs,
+ status: programsStatus,
+ loadMore: programsLoadMore,
+ } = usePaginatedQuery(
+ api.programs.getPrograms,
+ isAuthenticated ? { query: programsQuery } : ("skip" as const),
+ { initialNumItems: 20 },
+ );
+
+ const programOptions = React.useMemo(
+ () =>
+ (programs ?? []).map((program) => ({
+ value: program._id,
+ label: program.name,
+ })),
+ [programs],
+ );
+
+ const handleSearchPrograms = React.useCallback(
+ async (value: string) => {
+ const trimmed = value.trim();
+ setProgramsQuery(trimmed.length === 0 ? undefined : trimmed);
+ return programOptions;
+ },
+ [programOptions],
+ );
+
+ const handleLoadMorePrograms = React.useCallback(() => {
+ if (programsStatus === "CanLoadMore") {
+ void programsLoadMore(10);
+ }
+ }, [programsStatus, programsLoadMore]);
+
+ const currentYear = React.useMemo(() => new Date().getFullYear(), []);
+ const defaultTerm = React.useMemo(() => {
+ const month = new Date().getMonth();
+ return month >= 6 ? "fall" : "spring";
+ }, []);
+ const defaultStartingDate = {
+ year: currentYear,
+ term: defaultTerm,
+ };
+ const defaultExpectedGraduation = getTermAfterSemesters(
+ {
+ term: defaultTerm,
+ year: currentYear,
+ },
+ 14,
+ );
+
+ // Generate year options: currentYear ± 4 years
+ const yearOptions = React.useMemo(() => {
+ const years: number[] = [];
+ for (let i = currentYear - 5; i <= currentYear + 5; i++) {
+ years.push(i);
+ }
+ return years;
+ }, [currentYear]);
+
+ const form = useForm({
+ defaultValues: {
+ // student data
+ school: undefined as Id<"schools"> | undefined,
+ programs: [] as Id<"programs">[],
+ startingDate: defaultStartingDate as Doc<"students">["startingDate"],
+ expectedGraduationDate:
+ defaultExpectedGraduation as Doc<"students">["expectedGraduationDate"],
+ // user courses
+ userCourses: undefined as
+ | FunctionArgs["courses"]
+ | undefined,
+ },
+ validators: {
+ onSubmit: ({ value }) => {
+ const result = onboardingFormSchema.safeParse(value);
+ if (!result.success) {
+ const fieldErrors: Record = {};
+ for (const issue of result.error.issues) {
+ const path = issue.path.join(".");
+ fieldErrors[path] = [{ message: issue.message }];
+ }
+ return {
+ fields: fieldErrors,
+ };
+ }
+ return undefined;
+ },
+ },
+ onSubmit: async ({ value }) => {
+ try {
+ toast.success("Onboarding completed.");
+ router.push("/dashboard");
+
+ await upsertStudent({
+ school: value.school as Id<"schools">,
+ programs: value.programs,
+ startingDate: value.startingDate,
+ expectedGraduationDate: value.expectedGraduationDate,
+ });
+
+ if (value.userCourses) {
+ await importUserCourses({ courses: value.userCourses });
+ }
+ } catch (error) {
+ console.error("Error completing onboarding:", error);
+ toast.error("Could not complete onboarding. Please try again.");
+ }
+ },
+ });
+
+ function handleConfirmImport(
+ coursesToImport: UserCourse[],
+ startingTerm: StartingTerm | null,
+ ) {
+ if (coursesToImport.length === 0) {
+ return;
+ }
+
+ form.setFieldValue("userCourses", coursesToImport);
+
+ if (startingTerm) {
+ form.setFieldValue("startingDate", startingTerm);
+ form.setFieldValue(
+ "expectedGraduationDate",
+ getTermAfterSemesters(startingTerm, 14),
+ );
+ }
+
+ setIsFileLoaded(true);
+ }
+
+ function Form() {
+ return (
+
+ )
+ }
+
+ return (
+
+
+ );
+}
diff --git a/apps/web/src/app/dashboard/account/page.tsx b/apps/web/src/app/dashboard/account/page.tsx
index b5c59c43..a1e306e1 100644
--- a/apps/web/src/app/dashboard/account/page.tsx
+++ b/apps/web/src/app/dashboard/account/page.tsx
@@ -3,19 +3,10 @@
import { api } from "@albert-plus/server/convex/_generated/api";
import { useUser } from "@clerk/nextjs";
import { useConvexAuth, useMutation, useQuery } from "convex/react";
-import {
- Dialog,
- DialogClose,
- DialogContent,
- DialogDescription,
- DialogFooter,
- DialogHeader,
- DialogTitle,
- DialogTrigger,
-} from "@/components/ui/dialog"
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
+import {EditProfilePopup} from "./components/editProfile";
export default function ProfilePage() {
const { isAuthenticated } = useConvexAuth();
@@ -27,14 +18,6 @@ export default function ProfilePage() {
);
const upsert = useMutation(api.students.upsertCurrentStudent);
-
- if (student) {
- console.log("STUDENTTT");
- console.log(student.programs);
- } else{
- console.log("STUDENT NULL");
- }
-
return (
@@ -68,39 +51,7 @@ export default function ProfilePage() {
>
)}
-
-
+
);
}
From 2af87c7d79a141023683b1991f3db3a4fdd46bf4 Mon Sep 17 00:00:00 2001
From: Emily Silkina
Date: Thu, 13 Nov 2025 13:23:49 -0500
Subject: [PATCH 11/39] Populated edit profile with existing user data
---
.../account/components/editProfile.tsx | 64 +++++++++++++------
.../onboarding/component/onboarding-form.tsx | 2 +-
2 files changed, 44 insertions(+), 22 deletions(-)
diff --git a/apps/web/src/app/dashboard/account/components/editProfile.tsx b/apps/web/src/app/dashboard/account/components/editProfile.tsx
index b8273edf..8d63f75d 100644
--- a/apps/web/src/app/dashboard/account/components/editProfile.tsx
+++ b/apps/web/src/app/dashboard/account/components/editProfile.tsx
@@ -59,6 +59,7 @@ import {
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"
+import { useUser } from "@clerk/nextjs";
const dateSchema = z.object({
year: z.number().int().min(2000).max(2100),
@@ -107,6 +108,13 @@ export function EditProfilePopup() {
const [isFileLoaded, setIsFileLoaded] = React.useState(false);
const [currentStep, setCurrentStep] = React.useState<1 | 2>(1);
+ const { user } = useUser();
+
+ const student = useQuery(
+ api.students.getCurrentStudent,
+ isAuthenticated ? {} : "skip",
+ );
+
// actions
const upsertStudent = useMutation(api.students.upsertCurrentStudent);
const importUserCourses = useMutation(api.userCourses.importUserCourses);
@@ -232,6 +240,29 @@ export function EditProfilePopup() {
},
});
+ React.useEffect(() => {
+ if (!student) return;
+
+ // school: student.school can be an object or null
+ form.setFieldValue("school", student.school?._id ?? undefined);
+
+ // programs: student.programs might be an array of objects or array of ids
+ const programIds: Id<"programs">[] =
+ (student.programs ?? []).map((p: any) =>
+ typeof p === "string" ? (p as Id<"programs">) : (p?._id as Id<"programs">),
+ );
+
+ form.setFieldValue("programs", programIds);
+
+ // dates — assume these are already shaped correctly
+ if (student.startingDate) {
+ form.setFieldValue("startingDate", student.startingDate);
+ }
+ if (student.expectedGraduationDate) {
+ form.setFieldValue("expectedGraduationDate", student.expectedGraduationDate);
+ }
+ }, [student]);
+
function handleConfirmImport(
coursesToImport: UserCourse[],
startingTerm: StartingTerm | null,
@@ -263,7 +294,7 @@ export function EditProfilePopup() {
}}
className="space-y-6"
>
-
+ {/*
@@ -343,9 +374,9 @@ export function EditProfilePopup() {
-
+ */}
-
+
{/*
Academic Information
@@ -362,7 +393,7 @@ export function EditProfilePopup() {
return (
- What school or college of NYU do you go to?
+ What school or college of NYU do you attend?
- What's your major(s) and minor(s)?
+ What are your major(s) and minor(s)?
-
-
-
-
-
-
+
+
+
@@ -540,12 +562,12 @@ export function EditProfilePopup() {
*/}
-
+ {/*
-
+ */}
diff --git a/apps/web/src/app/onboarding/component/onboarding-form.tsx b/apps/web/src/app/onboarding/component/onboarding-form.tsx
index 33df742b..b0f95e74 100644
--- a/apps/web/src/app/onboarding/component/onboarding-form.tsx
+++ b/apps/web/src/app/onboarding/component/onboarding-form.tsx
@@ -351,7 +351,7 @@ export function OnboardingForm() {
return (
- What school or college of NYU do you go to?
+ What school or college of NYU do you attend?
Date: Thu, 13 Nov 2025 16:00:06 -0500
Subject: [PATCH 12/39] Added degree progress upload
---
.../account/components/editProfile.tsx | 109 +++++++++++++++++-
1 file changed, 108 insertions(+), 1 deletion(-)
diff --git a/apps/web/src/app/dashboard/account/components/editProfile.tsx b/apps/web/src/app/dashboard/account/components/editProfile.tsx
index 8d63f75d..c3eda511 100644
--- a/apps/web/src/app/dashboard/account/components/editProfile.tsx
+++ b/apps/web/src/app/dashboard/account/components/editProfile.tsx
@@ -284,6 +284,78 @@ export function EditProfilePopup() {
setIsFileLoaded(true);
}
+ function DegreeProgressUpload() {
+ return (
+
+ )
+ }
+
function Form() {
return (
-
+
+
+
+
);
}
From 163a99eb716a2156792c40563c6b2909604c6e80 Mon Sep 17 00:00:00 2001
From: Emily Silkina
Date: Thu, 13 Nov 2025 18:40:19 -0500
Subject: [PATCH 13/39] Updated toast
---
apps/web/src/app/dashboard/account/components/editProfile.tsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/apps/web/src/app/dashboard/account/components/editProfile.tsx b/apps/web/src/app/dashboard/account/components/editProfile.tsx
index c3eda511..a6a9d613 100644
--- a/apps/web/src/app/dashboard/account/components/editProfile.tsx
+++ b/apps/web/src/app/dashboard/account/components/editProfile.tsx
@@ -220,8 +220,8 @@ export function EditProfilePopup() {
},
onSubmit: async ({ value }) => {
try {
- toast.success("Onboarding completed.");
- router.push("/dashboard");
+ toast.success("Successfully updated profile.");
+ // router.push("/dashboard");
await upsertStudent({
school: value.school as Id<"schools">,
From 11cfe85838c237d733c93d70bc2d8d7f6f748fad Mon Sep 17 00:00:00 2001
From: Chenxin Yan
Date: Thu, 13 Nov 2025 20:40:04 -0500
Subject: [PATCH 14/39] lint
---
.../account/components/editProfile.tsx | 540 +++++++++---------
1 file changed, 273 insertions(+), 267 deletions(-)
diff --git a/apps/web/src/app/dashboard/account/components/editProfile.tsx b/apps/web/src/app/dashboard/account/components/editProfile.tsx
index a6a9d613..aaa76fc8 100644
--- a/apps/web/src/app/dashboard/account/components/editProfile.tsx
+++ b/apps/web/src/app/dashboard/account/components/editProfile.tsx
@@ -58,7 +58,7 @@ import {
DialogHeader,
DialogTitle,
DialogTrigger,
-} from "@/components/ui/dialog"
+} from "@/components/ui/dialog";
import { useUser } from "@clerk/nextjs";
const dateSchema = z.object({
@@ -108,12 +108,12 @@ export function EditProfilePopup() {
const [isFileLoaded, setIsFileLoaded] = React.useState(false);
const [currentStep, setCurrentStep] = React.useState<1 | 2>(1);
- const { user } = useUser();
+ const { user } = useUser();
- const student = useQuery(
- api.students.getCurrentStudent,
- isAuthenticated ? {} : "skip",
- );
+ const student = useQuery(
+ api.students.getCurrentStudent,
+ isAuthenticated ? {} : "skip",
+ );
// actions
const upsertStudent = useMutation(api.students.upsertCurrentStudent);
@@ -247,21 +247,26 @@ export function EditProfilePopup() {
form.setFieldValue("school", student.school?._id ?? undefined);
// programs: student.programs might be an array of objects or array of ids
- const programIds: Id<"programs">[] =
- (student.programs ?? []).map((p: any) =>
- typeof p === "string" ? (p as Id<"programs">) : (p?._id as Id<"programs">),
- );
+ const programIds: Id<"programs">[] = (student.programs ?? []).map(
+ (p: any) =>
+ typeof p === "string"
+ ? (p as Id<"programs">)
+ : (p?._id as Id<"programs">),
+ );
form.setFieldValue("programs", programIds);
// dates — assume these are already shaped correctly
if (student.startingDate) {
- form.setFieldValue("startingDate", student.startingDate);
+ form.setFieldValue("startingDate", student.startingDate);
}
if (student.expectedGraduationDate) {
- form.setFieldValue("expectedGraduationDate", student.expectedGraduationDate);
+ form.setFieldValue(
+ "expectedGraduationDate",
+ student.expectedGraduationDate,
+ );
}
- }, [student]);
+ }, [student]);
function handleConfirmImport(
coursesToImport: UserCourse[],
@@ -286,87 +291,86 @@ export function EditProfilePopup() {
function DegreeProgressUpload() {
return (
-
- )
+
+
+
+
+
+ );
}
function Form() {
return (
-