@@ -5,14 +5,15 @@ import * as api from '../lib/api';
55import { Navbar } from '../components/Navbar' ;
66import { Download , MapPin , Briefcase , GraduationCap , Mail , Github , Linkedin , Twitter , Instagram , Youtube , Code , Smartphone , Cloud , Terminal , Layout , Database , Zap } from 'lucide-react' ;
77import { motion } from 'framer-motion' ;
8+ import ReactMarkdown from 'react-markdown' ;
9+ import { ensureFullUrl } from '../lib/utils' ;
810
911const IconMap : { [ key : string ] : React . ElementType } = {
10- code : Code ,
11- smartphone : Smartphone ,
12- cloud : Cloud ,
13- terminal : Terminal ,
14- layout : Layout ,
15- database : Database
12+ code : Code , smartphone : Smartphone , cloud : Cloud , terminal : Terminal , layout : Layout , database : Database
13+ } ;
14+
15+ const SocialIconMap : { [ key : string ] : React . ElementType } = {
16+ github : Github , linkedin : Linkedin , twitter : Twitter , instagram : Instagram , youtube : Youtube , mail : Mail ,
1617} ;
1718
1819export default function AboutPage ( ) {
@@ -26,80 +27,76 @@ export default function AboutPage() {
2627 const isLoading = isLoadingProfile || isLoadingExp || isLoadingEdu || isLoadingServices ;
2728 const statsTheme = theme === 'dark' ? 'dark' : 'default' ;
2829
29- // const getGithubUsername = (url: string | undefined) => {
30- // if (!url) return '';
31- // const cleanUrl = url.replace(/\/$/, ''); // Remove trailing slash
32- // return cleanUrl.split('/').pop();
33- // };
34- // const username = getGithubUsername(profile?.socials.github);
30+ const getGithubUsername = ( url : string | undefined ) => {
31+ if ( ! url ) return '' ;
32+ try {
33+ const urlObj = new URL ( url ) ;
34+ return urlObj . pathname . substring ( 1 ) ;
35+ } catch ( e ) {
36+ return '' ;
37+ }
38+ } ;
39+
40+ const username = getGithubUsername ( ensureFullUrl ( ( profile ?. social_links as any ) ?. github ) ) ;
3541
3642 if ( isLoading ) {
37- return (
38- < div className = "min-h-screen bg-slate-50 dark:bg-slate-950 flex items-center justify-center" >
39- < p > Loading...</ p >
40- </ div >
41- )
43+ return < div className = "min-h-screen bg-slate-50 dark:bg-slate-950 flex items-center justify-center" > < p > Loading...</ p > </ div >
4244 }
4345
4446 return (
4547 < div className = "min-h-screen bg-slate-50 dark:bg-slate-950 text-slate-900 dark:text-white transition-colors duration-300" >
4648 < Navbar />
4749 < div className = "pt-32 pb-20 max-w-5xl mx-auto px-4 sm:px-6 lg:px-8" >
4850
49- { /* Header / Bio */ }
5051 < div className = "flex flex-col md:flex-row gap-12 items-start mb-20" >
5152 < div className = "w-full md:w-1/3 shrink-0" >
5253 < div className = "aspect-square rounded-2xl overflow-hidden border-4 border-white dark:border-slate-800 shadow-xl relative mb-6" >
5354 < div className = "absolute inset-0 bg-indigo-500/10 mix-blend-overlay" />
54- < img src = { profile ?. avatar_url || '' } alt = { profile ?. full_name || 'User' } className = "w-full h-full object-cover bg-white dark:bg-slate-900" />
55+ < img src = { profile ?. avatar_url || '' } alt = { profile ?. display_name || 'User' } className = "w-full h-full object-cover bg-white dark:bg-slate-900" />
5556 </ div >
5657
5758 < div className = "flex flex-col gap-4" >
58- { /* Placeholder for Address & Socials */ }
59+ { profile ?. address && (
60+ < div className = "flex items-center gap-3 text-slate-600 dark:text-slate-400" > < MapPin className = "h-5 w-5 text-indigo-500" /> < span > { profile . address } </ span > </ div >
61+ ) }
62+
63+ < div className = "flex flex-wrap gap-4 mt-2" >
64+ { profile ?. social_links && typeof profile . social_links === 'object' && Object . entries ( profile . social_links ) . map ( ( [ key , value ] ) => {
65+ if ( ! value ) return null ;
66+ const Icon = SocialIconMap [ key ] ;
67+ if ( ! Icon ) return null ;
68+ const href = key === 'mail' ? `mailto:${ value } ` : ensureFullUrl ( value as string ) || '#' ;
69+ return (
70+ < a key = { key } href = { href } target = "_blank" rel = "noreferrer" className = "p-2 bg-slate-200 dark:bg-slate-900 rounded-lg hover:bg-indigo-100 dark:hover:bg-indigo-900/50 text-slate-600 dark:text-slate-400 hover:text-indigo-600 dark:hover:text-indigo-400 transition-colors" >
71+ < Icon className = "h-5 w-5" />
72+ </ a >
73+ )
74+ } ) }
75+ </ div >
5976 </ div >
6077
61- < a
62- href = "#" // Placeholder for resume_url
63- target = "_blank"
64- className = "mt-8 w-full flex items-center justify-center gap-2 bg-indigo-600 hover:bg-indigo-700 text-white py-3 rounded-lg font-medium transition-all shadow-lg shadow-indigo-500/20"
65- >
78+ < a href = { ensureFullUrl ( profile ?. resume_url ) || '#' } target = "_blank" rel = "noopener noreferrer" className = "mt-8 w-full flex items-center justify-center gap-2 bg-indigo-600 hover:bg-indigo-700 text-white py-3 rounded-lg font-medium transition-all shadow-lg shadow-indigo-500/20" >
6679 < Download className = "h-5 w-5" /> Download CV
6780 </ a >
6881 </ div >
6982
7083 < div className = "flex-1" >
71- < h1 className = "text-4xl font-bold mb-6" > About Me</ h1 >
84+ < h1 className = "text-4xl font-bold mb-6" > { profile ?. display_name || ' About Me' } </ h1 >
7285 < div className = "prose prose-lg prose-slate dark:prose-invert" >
73- < p className = "whitespace-pre-wrap leading-relaxed text-slate-600 dark:text-slate-300" >
74- { /* Placeholder for detailed_bio */ }
75- </ p >
86+ < ReactMarkdown > { profile ?. detailed_bio || '' } </ ReactMarkdown >
7687 </ div >
7788 </ div >
7889 </ div >
7990
80- { /* Services Section */ }
8191 { services && services . length > 0 && (
8292 < div className = "mb-20" >
83- < h2 className = "text-2xl font-bold mb-8 flex items-center gap-3" >
84- < div className = "p-2 bg-yellow-100 dark:bg-yellow-900/30 rounded-lg text-yellow-600 dark:text-yellow-400" >
85- < Zap className = "h-6 w-6" />
86- </ div >
87- What I Do
88- </ h2 >
93+ < h2 className = "text-2xl font-bold mb-8 flex items-center gap-3" > < div className = "p-2 bg-yellow-100 dark:bg-yellow-900/30 rounded-lg text-yellow-600 dark:text-yellow-400" > < Zap className = "h-6 w-6" /> </ div > What I Do</ h2 >
8994 < div className = "grid grid-cols-1 md:grid-cols-2 gap-6" >
9095 { services . map ( ( service , idx ) => {
9196 const Icon = service . icon_name ? IconMap [ service . icon_name ] || Code : Code ;
9297 return (
93- < motion . div
94- key = { service . id }
95- initial = { { opacity : 0 , y : 20 } }
96- whileInView = { { opacity : 1 , y : 0 } }
97- transition = { { delay : idx * 0.1 } }
98- className = "bg-white border border-slate-200 dark:bg-slate-900/50 dark:border-white/5 p-6 rounded-xl hover:border-indigo-500/30 transition-all shadow-sm dark:shadow-none group"
99- >
100- < div className = "h-12 w-12 bg-indigo-50 dark:bg-slate-800 rounded-lg flex items-center justify-center text-indigo-600 dark:text-indigo-400 mb-4 group-hover:bg-indigo-600 group-hover:text-white transition-colors" >
101- < Icon className = "h-6 w-6" />
102- </ div >
98+ < motion . div key = { service . id } initial = { { opacity : 0 , y : 20 } } whileInView = { { opacity : 1 , y : 0 } } transition = { { delay : idx * 0.1 } } className = "bg-white border border-slate-200 dark:bg-slate-900/50 dark:border-white/5 p-6 rounded-xl hover:border-indigo-500/30 transition-all shadow-sm dark:shadow-none group" >
99+ < div className = "h-12 w-12 bg-indigo-50 dark:bg-slate-800 rounded-lg flex items-center justify-center text-indigo-600 dark:text-indigo-400 mb-4 group-hover:bg-indigo-600 group-hover:text-white transition-colors" > < Icon className = "h-6 w-6" /> </ div >
103100 < h3 className = "text-lg font-bold mb-2 text-slate-900 dark:text-white" > { service . title } </ h3 >
104101 < p className = "text-slate-600 dark:text-slate-400 text-sm leading-relaxed" > { service . description } </ p >
105102 </ motion . div >
@@ -110,64 +107,47 @@ export default function AboutPage() {
110107 ) }
111108
112109 < div className = "grid md:grid-cols-2 gap-12 mb-20" >
113- { /* Experience */ }
114110 < div >
115- < h2 className = "text-2xl font-bold mb-8 flex items-center gap-3" >
116- < div className = "p-2 bg-indigo-100 dark:bg-indigo-900/30 rounded-lg text-indigo-600 dark:text-indigo-400" >
117- < Briefcase className = "h-6 w-6" />
118- </ div >
119- Experience
120- </ h2 >
111+ < h2 className = "text-2xl font-bold mb-8 flex items-center gap-3" > < div className = "p-2 bg-indigo-100 dark:bg-indigo-900/30 rounded-lg text-indigo-600 dark:text-indigo-400" > < Briefcase className = "h-6 w-6" /> </ div > Experience</ h2 >
121112 < div className = "space-y-8 border-l-2 border-slate-200 dark:border-slate-800 ml-3 pl-8 relative" >
122113 { experience ?. map ( ( exp , idx ) => (
123- < motion . div
124- key = { exp . id }
125- initial = { { opacity : 0 , x : - 20 } }
126- whileInView = { { opacity : 1 , x : 0 } }
127- transition = { { delay : idx * 0.1 } }
128- className = "relative"
129- >
114+ < motion . div key = { exp . id } initial = { { opacity : 0 , x : - 20 } } whileInView = { { opacity : 1 , x : 0 } } transition = { { delay : idx * 0.1 } } className = "relative" >
130115 < span className = "absolute -left-[41px] top-1 w-5 h-5 rounded-full border-4 border-white dark:border-slate-950 bg-indigo-500" />
131116 < h3 className = "text-xl font-bold text-slate-900 dark:text-white" > { exp . title } </ h3 >
132117 < div className = "text-indigo-600 dark:text-indigo-400 font-medium mb-2" > { exp . institution } </ div >
133- < div className = "text-sm text-slate-500 mb-3" > { exp . start_date } - { exp . end_date || 'Present' } </ div >
134- < p className = "text-slate-600 dark:text-slate-400 text-sm leading-relaxed mb-3" >
135- { exp . description }
136- </ p >
118+ < div className = "text-sm text-slate-500 mb-3" > { exp . period } </ div >
119+ < p className = "text-slate-600 dark:text-slate-400 text-sm leading-relaxed mb-3" > { exp . description } </ p >
137120 </ motion . div >
138121 ) ) }
139122 </ div >
140123 </ div >
141124
142- { /* Education */ }
143125 < div >
144- < h2 className = "text-2xl font-bold mb-8 flex items-center gap-3" >
145- < div className = "p-2 bg-purple-100 dark:bg-purple-900/30 rounded-lg text-purple-600 dark:text-purple-400" >
146- < GraduationCap className = "h-6 w-6" />
147- </ div >
148- Education
149- </ h2 >
126+ < h2 className = "text-2xl font-bold mb-8 flex items-center gap-3" > < div className = "p-2 bg-purple-100 dark:bg-purple-900/30 rounded-lg text-purple-600 dark:text-purple-400" > < GraduationCap className = "h-6 w-6" /> </ div > Education</ h2 >
150127 < div className = "space-y-8 border-l-2 border-slate-200 dark:border-slate-800 ml-3 pl-8 relative" >
151128 { education ?. map ( ( edu , idx ) => (
152- < motion . div
153- key = { edu . id }
154- initial = { { opacity : 0 , x : - 20 } }
155- whileInView = { { opacity : 1 , x : 0 } }
156- transition = { { delay : idx * 0.1 } }
157- className = "relative"
158- >
129+ < motion . div key = { edu . id } initial = { { opacity : 0 , x : - 20 } } whileInView = { { opacity : 1 , x : 0 } } transition = { { delay : idx * 0.1 } } className = "relative" >
159130 < span className = "absolute -left-[41px] top-1 w-5 h-5 rounded-full border-4 border-white dark:border-slate-950 bg-purple-500" />
160131 < h3 className = "text-xl font-bold text-slate-900 dark:text-white" > { edu . title } </ h3 >
161132 < div className = "text-purple-600 dark:text-purple-400 font-medium mb-1" > { edu . institution } </ div >
162- < div className = "text-sm text-slate-500 mb-2" > { edu . start_date } - { edu . end_date || 'Present' } </ div >
163- < p className = "text-sm text-slate-600 dark:text-slate-400 mb-3" >
164- { edu . description }
165- </ p >
133+ < div className = "text-sm text-slate-500 mb-2" > { edu . period } </ div >
134+ < p className = "text-sm text-slate-600 dark:text-slate-400 mb-3" > { edu . description } </ p >
166135 </ motion . div >
167136 ) ) }
168137 </ div >
169138 </ div >
170139 </ div >
140+
141+ { username && (
142+ < div className = "border-t border-slate-200 dark:border-white/10 pt-16" >
143+ < h2 className = "text-2xl font-bold mb-8 text-center text-slate-900 dark:text-white" > Coding Activity</ h2 >
144+ < div className = "flex flex-col md:flex-row gap-6 justify-center items-center" >
145+ < img src = { `https://github-readme-stats.vercel.app/api?username=${ username } &show_icons=true&theme=${ statsTheme } &bg_color=${ statsTheme === 'dark' ? '0f172a' : 'ffffff' } &title_color=${ statsTheme === 'dark' ? 'ffffff' : '0f172a' } &text_color=${ statsTheme === 'dark' ? '94a3b8' : '475569' } &icon_color=6366f1&border_color=${ statsTheme === 'dark' ? '1e293b' : 'e2e8f0' } ` } alt = "GitHub Stats" className = "rounded-xl border border-slate-200 dark:border-white/10 shadow-lg max-w-full" />
146+ < img src = { `https://github-readme-stats.vercel.app/api/top-langs/?username=${ username } &layout=compact&theme=${ statsTheme } &bg_color=${ statsTheme === 'dark' ? '0f172a' : 'ffffff' } &title_color=${ statsTheme === 'dark' ? 'ffffff' : '0f172a' } &text_color=${ statsTheme === 'dark' ? '94a3b8' : '475569' } &border_color=${ statsTheme === 'dark' ? '1e293b' : 'e2e8f0' } ` } alt = "Top Languages" className = "rounded-xl border border-slate-200 dark:border-white/10 shadow-lg max-w-full" />
147+ </ div >
148+ </ div >
149+ ) }
150+
171151 </ div >
172152 </ div >
173153 ) ;
0 commit comments