Skip to content

Commit 855c30f

Browse files
committed
extract ShareTwitterButton, ShareLinkedInButton, StarButton as generic components
1 parent c609224 commit 855c30f

4 files changed

Lines changed: 183 additions & 121 deletions

File tree

packages/app/src/components/footer/footer-star-cta.tsx

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,36 +3,34 @@
33
import { Button } from '@/components/ui/button';
44
import { track } from '@/lib/analytics';
55
import { Star } from 'lucide-react';
6-
import Link from 'next/link';
76

87
import { useGitHubStars } from '@/hooks/api/use-github-stars';
98

109
const GITHUB_REPO_URL = 'https://github.com/SemiAnalysisAI/InferenceX';
1110

12-
export function FooterStarButton() {
11+
export function StarButton() {
1312
const { data } = useGitHubStars();
1413
const stars = data?.stars ?? null;
1514

1615
return (
1716
<Button
1817
variant="outline"
1918
size="sm"
20-
className="h-7 gap-1.5 text-xs"
21-
asChild
19+
className="h-7 gap-1.5 text-xs star-button-glow"
2220
data-testid="footer-star-cta"
21+
onClick={() => {
22+
track('footer_star_cta_clicked', { stars: stars ?? 0 });
23+
window.open(GITHUB_REPO_URL, '_blank', 'noopener,noreferrer');
24+
}}
2325
>
24-
<Link
25-
href={GITHUB_REPO_URL}
26-
target="_blank"
27-
rel="noopener noreferrer"
28-
onClick={() => track('footer_star_cta_clicked', { stars: stars ?? 0 })}
29-
>
30-
<Star className="h-3.5 w-3.5 text-yellow-500 fill-yellow-500" />
31-
<span>Star</span>
32-
{stars !== null && (
33-
<span className="font-semibold text-muted-foreground">{stars.toLocaleString()}</span>
34-
)}
35-
</Link>
26+
<Star className="h-3.5 w-3.5 text-yellow-500 fill-yellow-500" />
27+
<span>Star</span>
28+
{stars !== null && (
29+
<span className="font-semibold text-muted-foreground">{stars.toLocaleString()}</span>
30+
)}
3631
</Button>
3732
);
3833
}
34+
35+
/** @deprecated Use StarButton instead */
36+
export const FooterStarButton = StarButton;

packages/app/src/components/footer/footer.tsx

Lines changed: 100 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
import Image from 'next/image';
22
import Link from 'next/link';
33

4-
import { SocialShareButtons } from '@/components/social-share-buttons';
54
import { cn } from '@/lib/utils';
65

7-
import { FooterStarButton } from './footer-star-cta';
6+
import { ShareTwitterButton, ShareLinkedInButton } from '@/components/share-buttons';
7+
8+
import { StarButton } from './footer-star-cta';
89

910
export const Footer = () => {
1011
return (
1112
<footer
1213
data-testid="footer"
1314
className={cn(
14-
'relative w-full overflow-visible',
15+
'relative w-full overflow-visible mt-auto',
1516
'before:absolute',
1617
'before:bg-muted/50',
1718
'dark:before:bg-muted',
@@ -29,68 +30,126 @@ export const Footer = () => {
2930
'before:-z-10',
3031
)}
3132
>
32-
<div className="container mx-auto py-8 px-4 flex flex-col items-center justify-center">
33-
{/* Open Source CTA */}
34-
<div className="flex flex-col items-center gap-3 mb-6 pb-6 border-b border-border/40">
35-
<div className="flex items-center gap-1.5">
36-
<FooterStarButton />
37-
<SocialShareButtons compact />
33+
<div className="container mx-auto px-4 lg:px-8 py-12">
34+
{/* Main grid */}
35+
<div className="grid grid-cols-1 md:grid-cols-3 gap-10 md:gap-8 mb-10">
36+
{/* Left — Brand */}
37+
<div className="flex flex-col gap-4">
38+
<Link target="_blank" href="https://semianalysis.com/">
39+
<Image
40+
width={140}
41+
height={58}
42+
src="/logo.png"
43+
alt="SemiAnalysis logo"
44+
className="hidden dark:block"
45+
/>
46+
<Image
47+
width={140}
48+
height={58}
49+
src="/logo-black.png"
50+
alt="SemiAnalysis logo"
51+
className="dark:hidden"
52+
/>
53+
</Link>
54+
<p className="text-sm text-muted-foreground max-w-xs">
55+
Open-source AI inference benchmarks. Transparent, real-world GPU performance data for
56+
the ML community.
57+
</p>
3858
</div>
39-
<p className="text-sm text-muted-foreground text-center max-w-md">
40-
InferenceX is open source. If this data helps your work, star us or share with your
41-
network.
42-
</p>
43-
</div>
4459

45-
{/* Policy Links */}
46-
<nav className="mb-4">
47-
<ul className="flex flex-wrap justify-center gap-x-4 gap-y-2 text-sm text-muted-foreground">
48-
<li>
60+
{/* Center — Links */}
61+
<div className="grid grid-cols-3 gap-6">
62+
<div className="flex flex-col gap-2.5">
63+
<span className="text-sm font-medium text-foreground">SemiAnalysis</span>
64+
<a
65+
href="https://semianalysis.com"
66+
target="_blank"
67+
rel="noopener noreferrer"
68+
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
69+
>
70+
Main Site
71+
</a>
72+
<a
73+
href="https://newsletter.semianalysis.com"
74+
target="_blank"
75+
rel="noopener noreferrer"
76+
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
77+
>
78+
Newsletter
79+
</a>
80+
<a
81+
href="https://semianalysis.com/about/"
82+
target="_blank"
83+
rel="noopener noreferrer"
84+
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
85+
>
86+
About
87+
</a>
88+
</div>
89+
<div className="flex flex-col gap-2.5">
90+
<span className="text-sm font-medium text-foreground">Legal</span>
4991
<a
5092
href="https://semianalysis.com/privacy-policy/"
51-
className="underline hover:text-foreground"
5293
target="_blank"
5394
rel="noopener noreferrer"
95+
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
5496
>
5597
Privacy Policy
5698
</a>
57-
</li>
58-
<li>
5999
<a
60100
href="https://semianalysis.com/cookie-policy/"
61-
className="underline hover:text-foreground"
62101
target="_blank"
63102
rel="noopener noreferrer"
103+
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
64104
>
65105
Cookie Policy
66106
</a>
67-
</li>
68-
<li>
107+
</div>
108+
<div className="flex flex-col gap-2.5">
109+
<span className="text-sm font-medium text-foreground">Contribute</span>
69110
<a
70111
href="https://github.com/SemiAnalysisAI/InferenceX"
71-
className="underline hover:text-foreground"
72112
target="_blank"
73113
rel="noopener noreferrer"
114+
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
74115
>
75-
Contribute
116+
Benchmarks
76117
</a>
77-
</li>
78-
</ul>
79-
</nav>
118+
<a
119+
href="https://github.com/SemiAnalysisAI/inferencemax-app"
120+
target="_blank"
121+
rel="noopener noreferrer"
122+
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
123+
>
124+
Frontend
125+
</a>
126+
</div>
127+
</div>
80128

81-
{/* Copyright Notice */}
82-
<p data-testid="footer-copyright" className="text-sm text-muted-foreground text-center">
83-
&copy; {new Date().getFullYear()} semianalysis.com All rights reserved.
84-
</p>
129+
{/* Right — CTA + Social */}
130+
<div className="flex flex-col gap-4 md:items-end">
131+
<div className="flex items-center gap-1.5">
132+
<div className="rounded-md bg-background/80">
133+
<StarButton />
134+
</div>
135+
<div className="rounded-md bg-background/80">
136+
<ShareTwitterButton />
137+
</div>
138+
<div className="rounded-md bg-background/80">
139+
<ShareLinkedInButton />
140+
</div>
141+
</div>
142+
<p className="text-sm text-muted-foreground md:text-right max-w-xs">
143+
If this data helps your work, star us or share with your network.
144+
</p>
145+
</div>
146+
</div>
85147

86-
{/* Logo Section */}
87-
<div className="mt-4 flex items-center justify-center">
88-
<Link target="_blank" className="hidden dark:block" href="https://semianalysis.com/">
89-
<Image width={184} height={76} src="/logo.png" alt="SemiAnalysis logo" />
90-
</Link>
91-
<Link target="_blank" className="dark:hidden" href="https://semianalysis.com/">
92-
<Image width={184} height={76} src="/logo-black.png" alt="SemiAnalysis logo" />
93-
</Link>
148+
{/* Bottom bar */}
149+
<div className="border-t border-border/40 pt-6 flex flex-col md:flex-row items-center justify-between gap-4">
150+
<p data-testid="footer-copyright" className="text-xs text-muted-foreground">
151+
&copy; {new Date().getFullYear()} semianalysis.com. All rights reserved.
152+
</p>
94153
</div>
95154
</div>
96155
</footer>
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
'use client';
2+
3+
import { Button } from '@/components/ui/button';
4+
import { track } from '@/lib/analytics';
5+
6+
const SITE_URL = 'https://inferencex.semianalysis.com';
7+
8+
const SHARE_TEXT =
9+
'Check out InferenceX — open-source ML inference benchmarks comparing GPUs across real-world workloads. Transparent, up-to-date data for the ML community.';
10+
11+
function getShareUrl(): string {
12+
if (typeof window === 'undefined') return SITE_URL;
13+
return window.location.href;
14+
}
15+
16+
export function ShareTwitterButton() {
17+
return (
18+
<Button
19+
variant="outline"
20+
size="icon"
21+
className="h-7 w-7"
22+
title="Share on X (Twitter)"
23+
data-testid="share-twitter"
24+
onClick={() => {
25+
const url = getShareUrl();
26+
window.open(
27+
`https://twitter.com/intent/tweet?text=${encodeURIComponent(SHARE_TEXT)}&url=${encodeURIComponent(url)}`,
28+
'_blank',
29+
'noopener,noreferrer,width=600,height=400',
30+
);
31+
track('social_share_twitter');
32+
}}
33+
>
34+
<svg viewBox="0 0 24 24" fill="currentColor" className="h-3.5 w-3.5">
35+
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" />
36+
</svg>
37+
</Button>
38+
);
39+
}
40+
41+
export function ShareLinkedInButton() {
42+
return (
43+
<Button
44+
variant="outline"
45+
size="icon"
46+
className="h-7 w-7"
47+
title="Share on LinkedIn"
48+
data-testid="share-linkedin"
49+
onClick={() => {
50+
const url = getShareUrl();
51+
window.open(
52+
`https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(url)}`,
53+
'_blank',
54+
'noopener,noreferrer,width=600,height=600',
55+
);
56+
track('social_share_linkedin');
57+
}}
58+
>
59+
<svg viewBox="0 0 24 24" fill="currentColor" className="h-3.5 w-3.5">
60+
<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z" />
61+
</svg>
62+
</Button>
63+
);
64+
}
Lines changed: 5 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,20 @@
11
'use client';
22

3-
import { track } from '@/lib/analytics';
4-
import { useCallback } from 'react';
5-
6-
import { Button } from '@/components/ui/button';
7-
8-
const SITE_URL = 'https://inferencex.semianalysis.com';
3+
import { ShareTwitterButton, ShareLinkedInButton } from '@/components/share-buttons';
94

105
interface SocialShareButtonsProps {
11-
/** Additional CSS classes */
126
className?: string;
13-
/** Compact mode: icon-only buttons */
147
compact?: boolean;
158
}
169

17-
function getShareText(): string {
18-
return 'Check out InferenceX — open-source ML inference benchmarks comparing GPUs across real-world workloads. Transparent, up-to-date data for the AI community.';
19-
}
20-
21-
function getShareUrl(): string {
22-
if (typeof window === 'undefined') return SITE_URL;
23-
return window.location.href;
24-
}
25-
2610
/**
27-
* Social share buttons for Twitter/X and LinkedIn.
28-
* Each share includes a pre-composed message promoting InferenceX.
11+
* @deprecated Use ShareTwitterButton and ShareLinkedInButton directly instead.
2912
*/
30-
export function SocialShareButtons({ className = '', compact = false }: SocialShareButtonsProps) {
31-
const shareToTwitter = useCallback(() => {
32-
const text = getShareText();
33-
const url = getShareUrl();
34-
const tweetUrl = `https://twitter.com/intent/tweet?text=${encodeURIComponent(text)}&url=${encodeURIComponent(url)}`;
35-
window.open(tweetUrl, '_blank', 'noopener,noreferrer,width=600,height=400');
36-
track('social_share_twitter');
37-
}, []);
38-
39-
const shareToLinkedIn = useCallback(() => {
40-
const url = getShareUrl();
41-
const linkedInUrl = `https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(url)}`;
42-
window.open(linkedInUrl, '_blank', 'noopener,noreferrer,width=600,height=600');
43-
track('social_share_linkedin');
44-
}, []);
45-
13+
export function SocialShareButtons({ className = '' }: SocialShareButtonsProps) {
4614
return (
4715
<div data-testid="social-share-buttons" className={`flex items-center gap-1.5 ${className}`}>
48-
{/* Twitter/X */}
49-
<Button
50-
variant="outline"
51-
size={compact ? 'icon' : 'sm'}
52-
className={compact ? 'h-7 w-7' : 'h-7 gap-1.5 text-xs'}
53-
onClick={shareToTwitter}
54-
title="Share on X (Twitter)"
55-
data-testid="share-twitter"
56-
>
57-
<svg viewBox="0 0 24 24" fill="currentColor" className="h-3.5 w-3.5">
58-
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" />
59-
</svg>
60-
{!compact && <span>Tweet</span>}
61-
</Button>
62-
63-
{/* LinkedIn */}
64-
<Button
65-
variant="outline"
66-
size={compact ? 'icon' : 'sm'}
67-
className={compact ? 'h-7 w-7' : 'h-7 gap-1.5 text-xs'}
68-
onClick={shareToLinkedIn}
69-
title="Share on LinkedIn"
70-
data-testid="share-linkedin"
71-
>
72-
<svg viewBox="0 0 24 24" fill="currentColor" className="h-3.5 w-3.5">
73-
<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z" />
74-
</svg>
75-
{!compact && <span>LinkedIn</span>}
76-
</Button>
16+
<ShareTwitterButton />
17+
<ShareLinkedInButton />
7718
</div>
7819
);
7920
}

0 commit comments

Comments
 (0)