Skip to content

Commit 082b5ff

Browse files
committed
comparative analysis share
1 parent 6ede310 commit 082b5ff

2 files changed

Lines changed: 80 additions & 11 deletions

File tree

app/(root)/dashboard/[username]/page.tsx

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,22 +60,27 @@ export default async function DashboardPage({
6060
searchParams,
6161
}: {
6262
params: Promise<{ username: string }>;
63-
searchParams: Promise<{ refresh?: string }>;
63+
searchParams: Promise<{
64+
refresh?: string;
65+
compare?: string;
66+
}>;
6467
}) {
6568
const { username } = await params;
66-
const refreshParams = await searchParams;
67-
const bypassCache = refreshParams?.refresh === 'true';
69+
const resolvedSearchParams = await searchParams;
70+
const bypassCache = resolvedSearchParams?.refresh === 'true';
71+
const compareUsername = resolvedSearchParams?.compare;
6872

6973
let data;
7074

7175
try {
7276
data = await getFullDashboardData(username, { bypassCache });
7377
} catch (error) {
7478
if (error instanceof Error && error.message.includes('not found')) {
75-
// Smart Redirect: If the GraphQL "user" query fails, check if it's actually an Organization
7679
let fallbackProfile;
7780
try {
78-
fallbackProfile = await fetchUserProfile(username, { bypassCache });
81+
fallbackProfile = await fetchUserProfile(username, {
82+
bypassCache,
83+
});
7984
} catch {
8085
return notFound();
8186
}
@@ -87,5 +92,26 @@ export default async function DashboardPage({
8792
throw error;
8893
}
8994

90-
return <DashboardClient initialData={data} username={username} />;
91-
}
95+
let compareData = null;
96+
97+
if (
98+
compareUsername &&
99+
compareUsername.toLowerCase() !== username.toLowerCase()
100+
) {
101+
try {
102+
compareData = await getFullDashboardData(compareUsername, {
103+
bypassCache,
104+
});
105+
} catch {
106+
compareData = null;
107+
}
108+
}
109+
110+
return (
111+
<DashboardClient
112+
initialData={data}
113+
username={username}
114+
compareData={compareData}
115+
/>
116+
);
117+
}

components/dashboard/DashboardClient.tsx

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import { useState, useEffect } from 'react';
44
import { AnimatePresence, motion } from 'framer-motion';
5-
import { X, RefreshCw } from 'lucide-react';
5+
import { X, RefreshCw, Share2 } from 'lucide-react';
66
import Link from 'next/link';
77
import { toast } from 'sonner';
88
import type { Achievement } from '@/types/dashboard';
@@ -19,6 +19,7 @@ import StatsCard from './StatsCard';
1919
import ComparisonStatsCard from './ComparisonStatsCard';
2020
import RadarChart from './RadarChart';
2121
import GrowthTrendChart from './GrowthTrendChart';
22+
import { useRouter } from 'next/navigation';
2223

2324
// Define the dashboard data structure
2425
interface DashboardData {
@@ -68,6 +69,7 @@ interface DashboardData {
6869
interface DashboardClientProps {
6970
initialData: DashboardData;
7071
username: string;
72+
compareData?: DashboardData | null;
7173
}
7274

7375
export interface ProfileMetrics {
@@ -311,13 +313,14 @@ function getPersonalityTags(
311313
// DashboardClient Component
312314
// ------------------------------------------------------------
313315

314-
export default function DashboardClient({ initialData, username }: DashboardClientProps) {
315-
const [secondUserData, setSecondUserData] = useState<DashboardData | null>(null);
316-
const [isCompareMode, setIsCompareMode] = useState(false);
316+
export default function DashboardClient({ initialData, username, compareData = null, }: DashboardClientProps) {
317+
const [secondUserData, setSecondUserData] = useState<DashboardData | null>(compareData);
318+
const [isCompareMode, setIsCompareMode] = useState(Boolean(compareData));
317319
const [isModalOpen, setIsModalOpen] = useState(false);
318320
const [secondUsernameInput, setSecondUsernameInput] = useState('');
319321
const [isLoadingSecond, setIsLoadingSecond] = useState(false);
320322
const [compareError, setCompareError] = useState<string | null>(null);
323+
const router = useRouter();
321324

322325
// Close modal on escape key
323326
useEffect(() => {
@@ -362,6 +365,11 @@ export default function DashboardClient({ initialData, username }: DashboardClie
362365
const data = await res.json();
363366
setSecondUserData(data);
364367
setIsCompareMode(true);
368+
369+
router.replace(
370+
`/dashboard/${username}?compare=${data.profile.username}`
371+
);
372+
365373
setIsModalOpen(false);
366374
toast.success(`Comparing ${username} vs ${data.profile.username}`);
367375
} catch (err: unknown) {
@@ -376,9 +384,33 @@ export default function DashboardClient({ initialData, username }: DashboardClie
376384
const handleExitCompare = () => {
377385
setIsCompareMode(false);
378386
setSecondUserData(null);
387+
388+
router.replace(`/dashboard/${username}`);
389+
379390
toast.info('Returned to single profile view');
380391
};
381392

393+
const handleShareComparison = async () => {
394+
if (!secondUserData) return;
395+
396+
const compareUrl = `${window.location.origin}/dashboard/${username}?compare=${secondUserData.profile.username}`;
397+
398+
try {
399+
if (navigator.share) {
400+
await navigator.share({
401+
title: `${username} vs ${secondUserData.profile.username}`,
402+
text: 'Check out this GitHub profile comparison',
403+
url: compareUrl,
404+
});
405+
} else {
406+
await navigator.clipboard.writeText(compareUrl);
407+
toast.success('Comparison link copied!');
408+
}
409+
} catch {
410+
// user cancelled share dialog
411+
}
412+
};
413+
382414
// ------------------------------------------------------------
383415
// Compare Mode Statistics Calculations
384416
// ------------------------------------------------------------
@@ -477,6 +509,17 @@ export default function DashboardClient({ initialData, username }: DashboardClie
477509
Compare Profile
478510
</button>
479511
)}
512+
{isCompareMode && secondUserData && (
513+
<button
514+
onClick={handleShareComparison}
515+
className="flex items-center gap-2 rounded-xl border border-black/10 dark:border-[rgba(255,255,255,0.15)] bg-blue-600 hover:bg-blue-700 px-4 py-2 text-sm font-semibold text-white transition-all duration-200 active:scale-[0.98]"
516+
>
517+
<Share2 size={16} />
518+
Share Comparison
519+
</button>
520+
)}
521+
522+
480523
<RefreshButton username={username} />
481524
<Link
482525
href="/"

0 commit comments

Comments
 (0)