Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
26 changes: 26 additions & 0 deletions __tests__/lib/profile-tabs.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { resolveProfileTab } from "@/lib/profile-tabs";
import { PROFILE_TABS } from "@/types/profile";

describe("resolveProfileTab", () => {
it("returns each valid tab id unchanged", () => {
for (const tab of PROFILE_TABS) {
expect(resolveProfileTab(tab.id)).toBe(tab.id);
}
});

it("defaults to command-center when the value is absent", () => {
expect(resolveProfileTab(undefined)).toBe("command-center");
});

it("defaults to command-center for garbage values", () => {
expect(resolveProfileTab("not-a-tab")).toBe("command-center");
expect(resolveProfileTab("")).toBe("command-center");
expect(resolveProfileTab("SETTINGS")).toBe("command-center");
});

it("uses the first entry when the query value is an array", () => {
expect(resolveProfileTab(["settings", "arsenal"])).toBe("settings");
expect(resolveProfileTab(["bogus", "settings"])).toBe("command-center");
expect(resolveProfileTab([])).toBe("command-center");
});
});
15 changes: 15 additions & 0 deletions src/lib/profile-tabs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { PROFILE_TABS, type ProfileTab } from "@/types/profile";

const VALID_TAB_IDS = new Set<string>(PROFILE_TABS.map((tab) => tab.id));

/**
* Resolves a `?tab=` query value to a valid ProfileTab.
* Falls back to "command-center" when the value is absent or invalid.
*/
export function resolveProfileTab(queryValue: string | string[] | undefined): ProfileTab {
const value = Array.isArray(queryValue) ? queryValue[0] : queryValue;
if (value && VALID_TAB_IDS.has(value)) {
return value as ProfileTab;
}
return "command-center";
}
14 changes: 11 additions & 3 deletions src/pages/profile/[id].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { useMount } from "@hooks";
import Layout01 from "@layout/layout-01";
import Spinner from "@ui/spinner";
import type { GetServerSideProps, NextPage } from "next";
import { useRouter } from "next/router";
import { getServerSession } from "next-auth/next";
import { signOut } from "next-auth/react";
import { useState } from "react";
import {
ActivityFeed,
GitHubReadme,
Expand All @@ -25,6 +25,7 @@ import useGitHubProfile from "@/hooks/use-github-profile";
import useLearningStats from "@/hooks/use-learning-stats";
import useProfileForm from "@/hooks/use-profile-form";
import prisma from "@/lib/prisma";
import { resolveProfileTab } from "@/lib/profile-tabs";
import { options } from "@/pages/api/auth/options";
import type { ProfileTab, ProfileUser } from "@/types/profile";

Expand All @@ -44,7 +45,10 @@ type PageWithLayout = NextPage<PageProps> & {

const MemberProfile: PageWithLayout = ({ user, isOwner }) => {
const mounted = useMount();
const [activeTab, setActiveTab] = useState<ProfileTab>("command-center");
const router = useRouter();
const requestedTab = resolveProfileTab(router.query.tab);
// Settings is owner-only; fall back for non-owners deep-linking ?tab=settings
const activeTab = requestedTab === "settings" && !isOwner ? "command-center" : requestedTab;

// Pass the target user's ID to hooks so they fetch that member's data
const targetId = isOwner ? undefined : user.id;
Expand All @@ -60,6 +64,10 @@ const MemberProfile: PageWithLayout = ({ user, isOwner }) => {
);
}

const handleTabChange = (tab: ProfileTab) => {
void router.push({ query: { ...router.query, tab } }, undefined, { shallow: true });
};

const handleLogout = async () => {
try {
await signOut({ callbackUrl: "/login" });
Expand Down Expand Up @@ -101,7 +109,7 @@ const MemberProfile: PageWithLayout = ({ user, isOwner }) => {
onLogout={handleLogout}
/>

<ProfileNav activeTab={activeTab} onTabChange={setActiveTab} isOwner={isOwner} />
<ProfileNav activeTab={activeTab} onTabChange={handleTabChange} isOwner={isOwner} />

{/* Command Center — GitHub overview */}
{activeTab === "command-center" && (
Expand Down
Loading