From ef099c69b2c71634478d315943d5fc3ca00f2e7e Mon Sep 17 00:00:00 2001 From: michelleokolie Date: Tue, 26 May 2026 09:41:47 -0500 Subject: [PATCH 01/12] made empty file --- app/profile/page.tsx | 89 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 app/profile/page.tsx diff --git a/app/profile/page.tsx b/app/profile/page.tsx new file mode 100644 index 0000000..9e3836e --- /dev/null +++ b/app/profile/page.tsx @@ -0,0 +1,89 @@ +// app/profile/page.tsx +"use client"; + +import { useState, useEffect } from "react"; +import Navbar from "@/components/Navbar"; +import ProfileCard from "@/components/ProfileCard"; +import EditProfileModal from "@/components/EditProfileModal"; +import { supabase } from "@/lib/supabase"; +import { ProfileDTO } from "@/lib/dto/profiles"; +import { User } from "@supabase/supabase-js"; + +const EMPTY_PROFILE: ProfileDTO = { + name: "", + grad_year: "", + linkedin: "", + github: "", + experiences: [], +}; + +export default function ProfilePage() { + const [user, setUser] = useState(null); + const [profile, setProfile] = useState(EMPTY_PROFILE); + const [modalOpen, setModalOpen] = useState(false); + + useEffect(() => { + supabase.auth.getSession().then(({ data }) => { + setUser(data.session?.user ?? null); + }); + + const { data: { subscription } } = supabase.auth.onAuthStateChange((_e, session) => { + setUser(session?.user ?? null); + }); + + return () => subscription.unsubscribe(); + }, []); + + function handleSave(data: ProfileDTO) { + setProfile(data); + // TODO: supabase upsert + } + + const hasProfile = !!profile.name; + + return ( +
+ + +
+ {!user ? ( +
+

+ Sign in to create your profile +

+
+ ) : ( +
+
+

Your Card

+ {!hasProfile && ( + + )} +
+ +
+ setModalOpen(true)} + /> +
+
+ )} +
+ + {modalOpen && ( + setModalOpen(false)} + /> + )} +
+ ); +} \ No newline at end of file From 3e50138d97812f80d2f1afbefd0f43f709da72d3 Mon Sep 17 00:00:00 2001 From: michelleokolie Date: Tue, 26 May 2026 09:42:10 -0500 Subject: [PATCH 02/12] removed redundant profile --- lib/dto/profiles.ts | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 lib/dto/profiles.ts diff --git a/lib/dto/profiles.ts b/lib/dto/profiles.ts deleted file mode 100644 index 51b5ec6..0000000 --- a/lib/dto/profiles.ts +++ /dev/null @@ -1,14 +0,0 @@ -export interface ExperienceDTO { - company: string; - role: string; -} - -export interface ProfileDTO { - id?: string; - name?: string; - start_term?: string; - grad_year?: string; - linkedin?: string; - github?: string; - experiences?: ExperienceDTO[]; -} From 5de4b3787df868020ce5edd1185a9ccaab1f79a2 Mon Sep 17 00:00:00 2001 From: michelleokolie Date: Tue, 26 May 2026 09:48:44 -0500 Subject: [PATCH 03/12] added helpwe methods for formatting --- components/ProfileCard.tsx | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/components/ProfileCard.tsx b/components/ProfileCard.tsx index 6fa67b6..bbca6ef 100644 --- a/components/ProfileCard.tsx +++ b/components/ProfileCard.tsx @@ -1,7 +1,7 @@ // Thank Peter for his pre-made components import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card"; import { Github, Linkedin, Pencil } from "lucide-react"; -import type { ProfileDTO } from "@/lib/dto/profiles"; +import type { ProfileDTO, Contact, Experience } from "@/lib/dto/profile"; interface ProfileCardProps { profile: ProfileDTO | null; @@ -9,6 +9,27 @@ interface ProfileCardProps { onEdit?: () => void; } +// Helper methods for clean formatting +function formatTerm(term: [string, number] | null | undefined, fallback: string): string { + if (!term) return fallback; + return `${term[0]} '${String(term[1]).slice(2)}`; +} + +function formatTermRange(profile: ProfileDTO): string { + const start = formatTerm(profile.startTerm, "?"); + const end = profile.endTerm ? formatTerm(profile.endTerm, "Present") : "Present"; + return `${start} — ${end}`; +} + +function getContact(contacts: Contact[], type: "LinkedIn" | "GitHub"): string | null { + const match = contacts.find(([t]) => t === type); + return match ? match[1] : null; +} + +function formatExperience(exp: Experience): { company: string; role: string } { + return { company: exp[2], role: exp[1] }; +} + // Destructure the prop, ignore all types for now unless we want to add an interface later on // Thank Peter for his pre-made components From e20a726766f13986abcf2ee09257cf031b92fd1e Mon Sep 17 00:00:00 2001 From: michelleokolie Date: Tue, 26 May 2026 09:56:25 -0500 Subject: [PATCH 04/12] made it look pretty :) --- components/ProfileCard.tsx | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/components/ProfileCard.tsx b/components/ProfileCard.tsx index bbca6ef..5d3bcbf 100644 --- a/components/ProfileCard.tsx +++ b/components/ProfileCard.tsx @@ -38,6 +38,9 @@ export default function ProfileCard({ profile, editable = false, onEdit }: Profi if (!profile) return null; const isProfileEmpty = !profile.name; + const linkedin = getContact(profile.contact ?? [], "LinkedIn"); + const github = getContact(profile.contact ?? [], "GitHub"); + const termRange = formatTermRange(profile); return ( @@ -52,15 +55,15 @@ export default function ProfileCard({ profile, editable = false, onEdit }: Profi
- {profile?.linkedin ? ( - + {linkedin ? ( + ) : ( )} - {profile?.github ? ( - + {github ? ( + ) : ( @@ -70,8 +73,8 @@ export default function ProfileCard({ profile, editable = false, onEdit }: Profi
- - {profile?.grad_year ?? "----"} + + {isProfileEmpty ? "---" : termRange} {editable && ( @@ -87,14 +90,17 @@ export default function ProfileCard({ profile, editable = false, onEdit }: Profi - {(profile.experiences ?? []).map((exp, i: number) => ( -
-

{exp.company}

-

{exp.role}

-
- ))} + {(profile.experience ?? []).map((exp, i) => { + const { company, role } = formatExperience(exp); + return ( +
+

{company}

+

{role}

+
+ ); + })} - {(!profile.experiences || profile.experiences.length === 0) && ( + {(!profile.experience || profile.experience.length === 0) && (

No history available

)}
From 4120b9d50e650505b741ded39d6eb3c4dffcac4f Mon Sep 17 00:00:00 2001 From: michelleokolie <69334813+michelleokolie@users.noreply.github.com> Date: Tue, 26 May 2026 12:48:02 -0500 Subject: [PATCH 05/12] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- app/profile/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/profile/page.tsx b/app/profile/page.tsx index 9e3836e..5a79736 100644 --- a/app/profile/page.tsx +++ b/app/profile/page.tsx @@ -6,7 +6,7 @@ import Navbar from "@/components/Navbar"; import ProfileCard from "@/components/ProfileCard"; import EditProfileModal from "@/components/EditProfileModal"; import { supabase } from "@/lib/supabase"; -import { ProfileDTO } from "@/lib/dto/profiles"; +import { ProfileDTO } from "@/lib/dto/profile"; import { User } from "@supabase/supabase-js"; const EMPTY_PROFILE: ProfileDTO = { From 992d8f065ea009ea0ae845df07f932501d6dd153 Mon Sep 17 00:00:00 2001 From: michelleokolie Date: Tue, 26 May 2026 16:00:23 -0500 Subject: [PATCH 06/12] added the main index page --- app/page.tsx | 180 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 129 insertions(+), 51 deletions(-) diff --git a/app/page.tsx b/app/page.tsx index ac33981..6b75d48 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,58 +1,136 @@ -import { DeployButton } from "@/components/deploy-button"; -import { EnvVarWarning } from "@/components/env-var-warning"; -import { AuthButton } from "@/components/auth-button"; -import { Hero } from "@/components/hero"; -import { ThemeSwitcher } from "@/components/theme-switcher"; -import { ConnectSupabaseSteps } from "@/components/tutorial/connect-supabase-steps"; -import { SignUpUserSteps } from "@/components/tutorial/sign-up-user-steps"; -import { hasEnvVars } from "@/lib/utils"; -import Link from "next/link"; -import { Suspense } from "react"; - -export default function Home() { +// app/page.tsx +"use client"; + +import { useState, useEffect, useMemo } from "react"; +import { createClient } from "@/lib/supabase/client"; +import Navbar from "@/components/Navbar"; +import ProfileCard from "@/components/ProfileCard"; +import { Search } from "lucide-react"; +import type { ProfileDTO } from "@/lib/dto/profile"; +import type { User } from "@supabase/supabase-js"; + +const supabase = createClient(); +const SESSION_KEY = "connectcs-profile-order"; + +function shuffle(arr: T[]): T[] { + const a = [...arr]; + for (let i = a.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [a[i], a[j]] = [a[j], a[i]]; + } + return a; +} + +function applyStoredOrder(source: ProfileDTO[]): ProfileDTO[] { + const stored = sessionStorage.getItem(SESSION_KEY); + + if (stored) { + const order: string[] = JSON.parse(stored); + const known = [...source].sort( + (a, b) => order.indexOf(a.userId) - order.indexOf(b.userId) + ); + // Any new profiles not in the stored order get appended at the end + const newProfiles = source.filter((p) => !order.includes(p.userId)); + if (newProfiles.length > 0) { + const updated = [...order, ...newProfiles.map((p) => p.userId)]; + sessionStorage.setItem(SESSION_KEY, JSON.stringify(updated)); + } + return [...known, ...newProfiles]; + } + + const shuffled = shuffle(source); + sessionStorage.setItem( + SESSION_KEY, + JSON.stringify(shuffled.map((p) => p.userId)) + ); + return shuffled; +} + +export default function HomePage() { + const [user, setUser] = useState(null); + const [profiles, setProfiles] = useState([]); + const [loading, setLoading] = useState(true); + const [query, setQuery] = useState(""); + + useEffect(() => { + supabase.auth.getSession().then(({ data }) => { + setUser(data.session?.user ?? null); + }); + + const { + data: { subscription }, + } = supabase.auth.onAuthStateChange((_e, session) => { + setUser(session?.user ?? null); + }); + + return () => subscription.unsubscribe(); + }, []); + + useEffect(() => { + async function fetchProfiles() { + setLoading(true); + + // SUPABASE CALL 3: fetch all profiles for the grid + const { data, error } = await supabase.from("profiles").select("*"); + const source: ProfileDTO[] = error ? [] : (data as ProfileDTO[]); + setProfiles(applyStoredOrder(source)); + setLoading(false); + } + + fetchProfiles(); + }, []); + + const filtered = useMemo(() => { + if (!query.trim()) return profiles; + return profiles.filter((p) => + p.name?.toLowerCase().includes(query.toLowerCase()) + ); + }, [profiles, query]); + return ( -
-
- -
- -
-

Next steps

- {hasEnvVars ? : } -
-
+
+ - + ) : ( +
+ {filtered.map((profile) => ( + + ))} +
+ )}
); -} +} \ No newline at end of file From c3680b8ae9b22ea4537d6612a2a9efd97299c197 Mon Sep 17 00:00:00 2001 From: michelleokolie Date: Tue, 26 May 2026 16:03:17 -0500 Subject: [PATCH 07/12] removed comments --- app/page.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/page.tsx b/app/page.tsx index 6b75d48..0411801 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -29,7 +29,6 @@ function applyStoredOrder(source: ProfileDTO[]): ProfileDTO[] { const known = [...source].sort( (a, b) => order.indexOf(a.userId) - order.indexOf(b.userId) ); - // Any new profiles not in the stored order get appended at the end const newProfiles = source.filter((p) => !order.includes(p.userId)); if (newProfiles.length > 0) { const updated = [...order, ...newProfiles.map((p) => p.userId)]; @@ -70,7 +69,6 @@ export default function HomePage() { async function fetchProfiles() { setLoading(true); - // SUPABASE CALL 3: fetch all profiles for the grid const { data, error } = await supabase.from("profiles").select("*"); const source: ProfileDTO[] = error ? [] : (data as ProfileDTO[]); setProfiles(applyStoredOrder(source)); From 1e9e17bbe02d2e39fe64dad087c3573075747116 Mon Sep 17 00:00:00 2001 From: michelleokolie Date: Tue, 26 May 2026 16:03:41 -0500 Subject: [PATCH 08/12] added navbar --- components/Navbar.tsx | 75 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 components/Navbar.tsx diff --git a/components/Navbar.tsx b/components/Navbar.tsx new file mode 100644 index 0000000..1d84566 --- /dev/null +++ b/components/Navbar.tsx @@ -0,0 +1,75 @@ +// components/Navbar.tsx +"use client"; + +import Link from "next/link"; +import { usePathname } from "next/navigation"; +import { createClient } from "@/lib/supabase/client"; +import type { User } from "@supabase/supabase-js"; + +export default function Navbar({ user }: { user: User | null }) { + const pathname = usePathname(); + const supabase = createClient(); + + async function handleSignIn() { + const email = prompt("Enter your email:"); + if (!email) return; + await supabase.auth.signInWithOtp({ email }); + alert("Check your email for a magic link."); + } + + async function handleSignOut() { + await supabase.auth.signOut(); + window.location.href = "/"; + } + + return ( + + ); +} + +function NavLink({ + href, + active, + children, +}: { + href: string; + active: boolean; + children: React.ReactNode; +}) { + return ( + + {children} + + ); +} \ No newline at end of file From 4eee5526d924261aad66955ca2c3bde4ed27e164 Mon Sep 17 00:00:00 2001 From: michelleokolie Date: Tue, 26 May 2026 16:04:56 -0500 Subject: [PATCH 09/12] redid the modal to play nicely with the profile page --- components/EditProfileModal.tsx | 520 ++++++++++++++++++++++---------- 1 file changed, 358 insertions(+), 162 deletions(-) diff --git a/components/EditProfileModal.tsx b/components/EditProfileModal.tsx index 3055209..89f89ab 100644 --- a/components/EditProfileModal.tsx +++ b/components/EditProfileModal.tsx @@ -1,187 +1,383 @@ +// components/EditProfileModal.tsx "use client"; -import { useState } from "react"; +import { useState, useEffect } from "react"; import { X } from "lucide-react"; -import { ProfileDTO, ExperienceDTO } from "@/lib/dto/profiles"; - +import { createClient } from "@/lib/supabase/client"; +import type { + ProfileDTO, + Term, + TermSeason, + Experience, + Contact, +} from "@/lib/dto/profile"; interface Props { - initial: ProfileDTO; - onSave: (data: ProfileDTO) => void; - onClose: () => void; + initial: ProfileDTO; + onSave: (data: ProfileDTO) => void; + onClose: () => void; +} + +const SEASONS: TermSeason[] = ["Fall", "Winter", "Summer"]; + +// Flat form shape — tuples are hard to bind to inputs directly +interface FormState { + name: string; + startSeason: TermSeason; + startYear: string; + endSeason: TermSeason; + endYear: string; + isPresent: boolean; + linkedin: string; + github: string; + experiences: { company: string; role: string }[]; +} + +// ProfileDTO → flat form +function profileToForm(p: ProfileDTO): FormState { + const linkedin = p.contact.find(([t]) => t === "LinkedIn")?.[1] ?? ""; + const github = p.contact.find(([t]) => t === "GitHub")?.[1] ?? ""; + return { + name: p.name ?? "", + startSeason: p.startTerm[0], + startYear: String(p.startTerm[1]), + endSeason: p.endTerm?.[0] ?? "Fall", + endYear: p.endTerm ? String(p.endTerm[1]) : "", + isPresent: !p.endTerm, + linkedin, + github, + experiences: p.experience.map((exp) => ({ + company: exp[2], + role: exp[1], + })), + }; } -// TODO: Replace with a supabase call to companies table -const COMPANIES = ["Ubisoft", "Bold Commerce", "SkipTheDishes", "AAFC", "Niche"]; +// Flat form → ProfileDTO +function formToProfile(form: FormState, original: ProfileDTO): ProfileDTO { + const contact: Contact[] = []; + if (form.linkedin) contact.push(["LinkedIn", form.linkedin]); + if (form.github) contact.push(["GitHub", form.github]); + + const experience: Experience[] = form.experiences.map(({ company, role }) => [ + "Other", // type — placeholder until form supports it + role, + company, + new Date(), // startDate — placeholder until form supports it + null, // endDate + ]); + + const startTerm: Term = [form.startSeason, Number(form.startYear)]; + const endTerm: Term | null = form.isPresent + ? null + : [form.endSeason, Number(form.endYear)]; + + return { + ...original, + name: form.name, + startTerm, + endTerm, + contact, + experience, + updatedAt: new Date(), + }; +} export default function EditProfileModal({ initial, onSave, onClose }: Props) { - const emptyProfile: ProfileDTO = { - name: "", - grad_year: "", - linkedin: "", - github: "", - experiences: [], - }; - - const [form, setForm] = useState(initial || emptyProfile); - - const [expInput, setExpInput] = useState({ company: "", role: "" }); - const [companyQuery, setCompanyQuery] = useState(""); - const [showSuggestions, setShowSuggestions] = useState(false); - - function handleField(field: keyof ProfileDTO, value: string) { - setForm((prev) => ({ ...prev, [field]: value })); + const [form, setForm] = useState(() => profileToForm(initial)); + const [companies, setCompanies] = useState([]); + const [companyQuery, setCompanyQuery] = useState(""); + const [expRole, setExpRole] = useState(""); + const [showSuggestions, setShowSuggestions] = useState(false); + const supabase = createClient(); + + // SUPABASE CALL 4: fetch existing companies for the dropdown + useEffect(() => { + async function fetchCompanies() { + const { data } = await supabase.from("companies").select("name"); + setCompanies(data?.map((c: { name: string }) => c.name) ?? []); } + fetchCompanies(); + }, []); - const suggestions = companyQuery.length > 0 ? COMPANIES.filter((c) => c.toLowerCase().includes(companyQuery.toLowerCase())) : []; - const canCreate = companyQuery.length > 0 && !COMPANIES.some((c) => c.toLowerCase() === companyQuery.toLowerCase()); + function setField(key: K, value: FormState[K]) { + setForm((prev) => ({ ...prev, [key]: value })); + } - function selectCompany(name: string) { - setExpInput((prev) => ({ ...prev, company: name })); - setCompanyQuery(name); - setShowSuggestions(false); - } + const suggestions = + companyQuery.length > 0 + ? companies.filter((c) => + c.toLowerCase().includes(companyQuery.toLowerCase()) + ) + : []; - function addExperience() { - if (!expInput.company) return; - setForm((prev) => ({ - ...prev, - experiences: [...prev.experiences ?? [], expInput], - })); - setExpInput({ company: "", role: "" }); - setCompanyQuery(""); - } + const canCreate = + companyQuery.length > 0 && + !companies.some((c) => c.toLowerCase() === companyQuery.toLowerCase()); - function removeExperience(i: number) { - setForm((prev) => ({ - ...prev, - experiences: (prev.experiences ?? []).filter((_, idx) => idx !== i), - })); - } + function selectCompany(name: string) { + setCompanyQuery(name); + setShowSuggestions(false); + } + + async function addExperience() { + if (!companyQuery.trim()) return; - function handleSave() { - // TODO: supabase call goes here to update or insert profile - onSave(form); - onClose(); + // SUPABASE CALL 5: register new company if it doesn't exist yet + if (canCreate) { + await supabase + .from("companies") + .upsert({ name: companyQuery }, { onConflict: "name" }); + setCompanies((prev) => [...prev, companyQuery]); } - return ( -
-
e.stopPropagation()} - > -
-

Edit Profile

- + setField("experiences", [ + ...form.experiences, + { company: companyQuery, role: expRole }, + ]); + setCompanyQuery(""); + setExpRole(""); + } + + function removeExperience(i: number) { + setField( + "experiences", + form.experiences.filter((_, idx) => idx !== i) + ); + } + + function handleSave() { + const dto = formToProfile(form, initial); + onSave(dto); + onClose(); + } + + return ( +
+
e.stopPropagation()} + > +
+

+ Edit Profile +

+ +
+ +
+
+

+ Info +

+ setField("name", v)} + /> + setField("linkedin", v)} + /> + setField("github", v)} + /> +
+ +
+

+ Term +

+ +
+ +
+ + setField("startYear", e.target.value)} + /> +
+
+ +
+
+ + +
+ + {!form.isPresent && ( +
+ + setField("endYear", e.target.value)} + />
+ )} +
+
-
-
-

Info

- handleField("name", v)} /> - handleField("grad_year", v)} /> - handleField("linkedin", v)} /> - handleField("github", v)} /> -
- -
-

Experience

- {(form.experiences ?? []).map((exp, i) => ( -
-
-

{exp.company}

-

{exp.role}

-
- -
- ))} - -
-
- { - setCompanyQuery(e.target.value); - setExpInput((p) => ({ ...p, company: e.target.value })); - setShowSuggestions(true); - }} - onFocus={() => setShowSuggestions(true)} - /> - - {showSuggestions && (suggestions.length > 0 || canCreate) && ( -
- {suggestions.map((c) => ( - - ))} - {canCreate && ( - - )} -
- )} -
- - setExpInput((p) => ({ ...p, role: e.target.value }))} - /> - - -
-
- - {/* Save */} - +
+

+ Experience +

+ + {form.experiences.map((exp, i) => ( +
+
+

{exp.company}

+

{exp.role}

+ +
+ ))} + +
+
+ { + setCompanyQuery(e.target.value); + setShowSuggestions(true); + }} + onFocus={() => setShowSuggestions(true)} + /> + {showSuggestions && (suggestions.length > 0 || canCreate) && ( +
+ {suggestions.map((c) => ( + + ))} + {canCreate && ( + + )} +
+ )} +
+ + setExpRole(e.target.value)} + /> + +
+
+ +
- ); +
+
+ ); } -function Field({ label, value, onChange }: { label: string; value: string; onChange: (v: string) => void }) { - return ( -
- - onChange(e.target.value)} - /> -
- ); +function Field({ + label, + value, + onChange, +}: { + label: string; + value: string; + onChange: (v: string) => void; +}) { + return ( +
+ + onChange(e.target.value)} + /> +
+ ); } \ No newline at end of file From 90e35d83766baa5987034adab2732810d8b613d0 Mon Sep 17 00:00:00 2001 From: michelleokolie Date: Tue, 26 May 2026 16:06:30 -0500 Subject: [PATCH 10/12] added missing attribute to exp. --- components/EditProfileModal.tsx | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/components/EditProfileModal.tsx b/components/EditProfileModal.tsx index 89f89ab..bdcb8fa 100644 --- a/components/EditProfileModal.tsx +++ b/components/EditProfileModal.tsx @@ -33,7 +33,6 @@ interface FormState { experiences: { company: string; role: string }[]; } -// ProfileDTO → flat form function profileToForm(p: ProfileDTO): FormState { const linkedin = p.contact.find(([t]) => t === "LinkedIn")?.[1] ?? ""; const github = p.contact.find(([t]) => t === "GitHub")?.[1] ?? ""; @@ -53,18 +52,17 @@ function profileToForm(p: ProfileDTO): FormState { }; } -// Flat form → ProfileDTO function formToProfile(form: FormState, original: ProfileDTO): ProfileDTO { const contact: Contact[] = []; if (form.linkedin) contact.push(["LinkedIn", form.linkedin]); if (form.github) contact.push(["GitHub", form.github]); const experience: Experience[] = form.experiences.map(({ company, role }) => [ - "Other", // type — placeholder until form supports it - role, + "Other", + role, company, - new Date(), // startDate — placeholder until form supports it - null, // endDate + new Date(), + null, ]); const startTerm: Term = [form.startSeason, Number(form.startYear)]; @@ -91,7 +89,6 @@ export default function EditProfileModal({ initial, onSave, onClose }: Props) { const [showSuggestions, setShowSuggestions] = useState(false); const supabase = createClient(); - // SUPABASE CALL 4: fetch existing companies for the dropdown useEffect(() => { async function fetchCompanies() { const { data } = await supabase.from("companies").select("name"); From 50b3a29e89679348d6ac36b34012ec629a5cb4fe Mon Sep 17 00:00:00 2001 From: michelleokolie Date: Tue, 26 May 2026 16:07:15 -0500 Subject: [PATCH 11/12] the profile page is done but it just looks pretty, doesnt work --- app/profile/page.tsx | 141 ++++++++++++++++++++++++++++--------------- 1 file changed, 93 insertions(+), 48 deletions(-) diff --git a/app/profile/page.tsx b/app/profile/page.tsx index 5a79736..26f250c 100644 --- a/app/profile/page.tsx +++ b/app/profile/page.tsx @@ -2,84 +2,129 @@ "use client"; import { useState, useEffect } from "react"; +import { createClient } from "@/lib/supabase/client"; import Navbar from "@/components/Navbar"; import ProfileCard from "@/components/ProfileCard"; import EditProfileModal from "@/components/EditProfileModal"; -import { supabase } from "@/lib/supabase"; -import { ProfileDTO } from "@/lib/dto/profile"; -import { User } from "@supabase/supabase-js"; - -const EMPTY_PROFILE: ProfileDTO = { - name: "", - grad_year: "", - linkedin: "", - github: "", - experiences: [], -}; +import type { ProfileDTO } from "@/lib/dto/profile"; +import type { User } from "@supabase/supabase-js"; + +const supabase = createClient(); + +function emptyProfile(userId: string): ProfileDTO { + return { + userId, + name: "", + startTerm: ["Fall", new Date().getFullYear()], + endTerm: null, + contact: [], + experience: [], + createdAt: new Date(), + updatedAt: new Date(), + }; +} export default function ProfilePage() { const [user, setUser] = useState(null); - const [profile, setProfile] = useState(EMPTY_PROFILE); + const [profile, setProfile] = useState(null); const [modalOpen, setModalOpen] = useState(false); + const [loading, setLoading] = useState(true); useEffect(() => { supabase.auth.getSession().then(({ data }) => { setUser(data.session?.user ?? null); }); - const { data: { subscription } } = supabase.auth.onAuthStateChange((_e, session) => { + const { + data: { subscription }, + } = supabase.auth.onAuthStateChange((_e, session) => { setUser(session?.user ?? null); }); return () => subscription.unsubscribe(); }, []); - function handleSave(data: ProfileDTO) { - setProfile(data); - // TODO: supabase upsert + useEffect(() => { + if (!user) { + setLoading(false); + return; + } + + async function fetchProfile() { + // SUPABASE CALL 1: fetch this user's own profile + // NOTE: if your Supabase column is "id" instead of "userId", change .eq("userId", ...) to .eq("id", ...) + const { data, error } = await supabase + .from("profiles") + .select("*") + .eq("userId", user!.id) + .single(); + + if (!error && data) setProfile(data as ProfileDTO); + setLoading(false); + } + + fetchProfile(); + }, [user]); + + async function handleSave(data: ProfileDTO) { + // SUPABASE CALL 2: upsert profile on save + // NOTE: same column name caveat as above applies here + const { error } = await supabase.from("profiles").upsert(data); + if (!error) setProfile(data); + } + + if (loading) { + return ( +
+ +
+
+
+
+ ); } - const hasProfile = !!profile.name; + if (!user) { + return ( +
+ +
+

+ Sign in to view your profile +

+
+
+ ); + } return (
-
- {!user ? ( -
-

- Sign in to create your profile -

-
- ) : ( -
-
-

Your Card

- {!hasProfile && ( - - )} -
- -
- setModalOpen(true)} - /> -
-
- )} +
+
+

Your Card

+ + setModalOpen(true)} + /> + + {!profile && ( + + )} +
{modalOpen && ( setModalOpen(false)} /> From 9380bf5bb0ced434f0c43b4d656e670441e3b341 Mon Sep 17 00:00:00 2001 From: michelleokolie Date: Tue, 26 May 2026 16:07:42 -0500 Subject: [PATCH 12/12] removed comemnts --- app/profile/page.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/profile/page.tsx b/app/profile/page.tsx index 26f250c..c0b65b2 100644 --- a/app/profile/page.tsx +++ b/app/profile/page.tsx @@ -51,8 +51,6 @@ export default function ProfilePage() { } async function fetchProfile() { - // SUPABASE CALL 1: fetch this user's own profile - // NOTE: if your Supabase column is "id" instead of "userId", change .eq("userId", ...) to .eq("id", ...) const { data, error } = await supabase .from("profiles") .select("*") @@ -67,8 +65,6 @@ export default function ProfilePage() { }, [user]); async function handleSave(data: ProfileDTO) { - // SUPABASE CALL 2: upsert profile on save - // NOTE: same column name caveat as above applies here const { error } = await supabase.from("profiles").upsert(data); if (!error) setProfile(data); }