11"use client"
22
3+ import { useState , useEffect , useRef } from "react"
34import Link from "next/link"
45import { ArrowLeft , Calendar , User } from "lucide-react"
56import Image from "next/image"
@@ -8,6 +9,15 @@ import Footer from "@/components/footer"
89import { BlogPost } from "@/lib/blog"
910
1011export default function BlogPostPage ( { post } : { post : BlogPost } ) {
12+ const [ copied , setCopied ] = useState ( false )
13+ const copiedTimeoutRef = useRef < number | null > ( null )
14+
15+ useEffect ( ( ) => {
16+ return ( ) => {
17+ if ( copiedTimeoutRef . current != null ) window . clearTimeout ( copiedTimeoutRef . current )
18+ }
19+ } , [ ] )
20+
1121 return (
1222 < div className = "min-h-screen bg-gradient-to-br from-green-50 via-yellow-50 to-[#FFC517]/10" >
1323 { /* Header */ }
@@ -29,15 +39,11 @@ export default function BlogPostPage({ post }: { post: BlogPost }) {
2939 { /* Hero Image */ }
3040 { post . image && (
3141 < div className = "relative h-64 md:h-96" >
32- < Image
33- src = {
34- post . image . startsWith ( "/" )
35- ? `${ post . image } `
36- : post . image
37- }
38- alt = { post . title }
39- fill
40- className = "object-cover"
42+ < Image
43+ src = { post . image }
44+ alt = { post . title }
45+ fill
46+ className = "object-cover"
4147 priority
4248 />
4349 < div className = "absolute inset-0 bg-gradient-to-t from-black/20 to-transparent" />
@@ -67,6 +73,129 @@ export default function BlogPostPage({ post }: { post: BlogPost }) {
6773 </ div >
6874 </ div >
6975
76+ { /* Social Share Buttons */ }
77+ < div className = "mb-8 pb-8 border-b border-gradient-to-r from-[#228B22]/20 to-[#FFBF00]/20" >
78+ < p className = "text-sm font-medium text-gray-600 mb-4" > Share this article:</ p >
79+ < div className = "flex flex-wrap items-center gap-3" >
80+ { /* Twitter/X Share */ }
81+ < button
82+ type = "button"
83+ onClick = { ( ) => {
84+ const text = encodeURIComponent ( post . title )
85+ const url = encodeURIComponent ( window . location . href )
86+ const shareWindow = window . open (
87+ `https://twitter.com/intent/tweet?text=${ text } &url=${ url } ` ,
88+ '_blank' ,
89+ 'noopener,noreferrer,width=550,height=420'
90+ )
91+ if ( ! shareWindow ) {
92+ alert ( 'Please allow popups to share this article.' )
93+ }
94+ } }
95+ className = "inline-flex items-center gap-2 px-4 py-2 rounded-full bg-gradient-to-r from-[#228B22] to-[#5A981A] hover:from-[#3E921E] hover:to-[#91A511] text-white text-sm font-medium transition-all duration-300 shadow-md hover:shadow-lg transform hover:-translate-y-0.5"
96+ aria-label = "Share on Twitter"
97+ >
98+ < svg className = "w-4 h-4" fill = "currentColor" viewBox = "0 0 24 24" >
99+ < 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" />
100+ </ svg >
101+ Twitter
102+ </ button >
103+
104+ { /* LinkedIn Share */ }
105+ < button
106+ type = "button"
107+ onClick = { ( ) => {
108+ const url = encodeURIComponent ( window . location . href )
109+ const title = encodeURIComponent ( post . title )
110+ const summary = encodeURIComponent ( post . excerpt || '' )
111+ const shareWindow = window . open (
112+ `https://www.linkedin.com/sharing/share-offsite/?url=${ url } &title=${ title } &summary=${ summary } ` ,
113+ '_blank' ,
114+ 'noopener,noreferrer,width=550,height=420'
115+ )
116+ if ( ! shareWindow ) {
117+ alert ( 'Please allow popups to share this article.' )
118+ }
119+ } }
120+ className = "inline-flex items-center gap-2 px-4 py-2 rounded-full bg-gradient-to-r from-[#91A511] to-[#ADAC0D] hover:from-[#ADAC0D] hover:to-[#C8B209] text-white text-sm font-medium transition-all duration-300 shadow-md hover:shadow-lg transform hover:-translate-y-0.5"
121+ aria-label = "Share on LinkedIn"
122+ >
123+ < svg className = "w-4 h-4" fill = "currentColor" viewBox = "0 0 24 24" >
124+ < 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" />
125+ </ svg >
126+ LinkedIn
127+ </ button >
128+
129+ { /* Facebook Share */ }
130+ < button
131+ type = "button"
132+ onClick = { ( ) => {
133+ const url = encodeURIComponent ( window . location . href )
134+ const quote = encodeURIComponent ( post . title )
135+ const shareWindow = window . open (
136+ `https://www.facebook.com/sharer/sharer.php?u=${ url } "e=${ quote } ` ,
137+ '_blank' ,
138+ 'noopener,noreferrer,width=550,height=420'
139+ )
140+ if ( ! shareWindow ) {
141+ alert ( 'Please allow popups to share this article.' )
142+ }
143+ } }
144+ className = "inline-flex items-center gap-2 px-4 py-2 rounded-full bg-gradient-to-r from-[#C8B209] to-[#E4B905] hover:from-[#E4B905] hover:to-[#FFBF00] text-white text-sm font-medium transition-all duration-300 shadow-md hover:shadow-lg transform hover:-translate-y-0.5"
145+ aria-label = "Share on Facebook"
146+ >
147+ < svg className = "w-4 h-4" fill = "currentColor" viewBox = "0 0 24 24" >
148+ < path d = "M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z" />
149+ </ svg >
150+ Facebook
151+ </ button >
152+
153+ { /* Copy Link */ }
154+ < button
155+ type = "button"
156+ onClick = { ( ) => {
157+ if ( ! window . isSecureContext || ! navigator . clipboard ?. writeText ) {
158+ alert ( 'Copy is only available in a secure context (HTTPS or localhost). Please copy the URL from the address bar.' )
159+ return
160+ }
161+ navigator . clipboard . writeText ( window . location . href )
162+ . then ( ( ) => {
163+ if ( copiedTimeoutRef . current != null ) window . clearTimeout ( copiedTimeoutRef . current )
164+ setCopied ( true )
165+ copiedTimeoutRef . current = window . setTimeout ( ( ) => setCopied ( false ) , 2000 )
166+ } )
167+ . catch ( ( err ) => {
168+ console . error ( 'Failed to copy:' , err )
169+ alert ( 'Failed to copy link. Please copy manually.' )
170+ } )
171+ } }
172+ className = "inline-flex items-center gap-2 px-4 py-2 rounded-full bg-gradient-to-r from-[#E4B905] to-[#FFBF00] hover:from-[#FFBF00] hover:to-[#FFC517] text-white text-sm font-medium transition-all duration-300 shadow-md hover:shadow-lg transform hover:-translate-y-0.5"
173+ aria-label = "Copy link to clipboard"
174+ >
175+ { copied ? (
176+ < >
177+ < svg className = "w-4 h-4" fill = "none" stroke = "currentColor" viewBox = "0 0 24 24" >
178+ < path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = { 2 } d = "M5 13l4 4L19 7" />
179+ </ svg >
180+ Copied!
181+ </ >
182+ ) : (
183+ < >
184+ < svg className = "w-4 h-4" fill = "none" stroke = "currentColor" viewBox = "0 0 24 24" >
185+ < path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = { 2 } d = "M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
186+ </ svg >
187+ Copy Link
188+ </ >
189+ ) }
190+ </ button >
191+ </ div >
192+ </ div >
193+
194+ { /* Screen reader announcement for copy feedback */ }
195+ < div role = "status" aria-live = "polite" className = "sr-only" >
196+ { copied && "Link copied to clipboard" }
197+ </ div >
198+
70199 < div className = "prose prose-lg max-w-none" >
71200 < MarkdownRenderer content = { post . content } />
72201 </ div >
0 commit comments