at main 4.4 kB view raw
1import { CardRole } from '@/components/CardRole/CardRole'; 2import { Divider } from '@/components/Divider/Divider'; 3import { Heading } from '@/components/Heading/Heading'; 4import { Aside, Layout, Main } from '@/components/Layout/Layout'; 5import { Link } from '@/components/Link/Link'; 6import { Paragraph } from '@/components/Paragraph/Paragraph'; 7import Section from '@/components/Section/Section'; 8import TLDRProfile from '@/components/TLDRProfile/TLDRProfile'; 9import { useProtopro } from '@/hooks/atproto'; 10import type { JSX } from 'react'; 11import { Helmet } from 'react-helmet-async'; 12 13/** 14 * Work page component - displays CV/work history from AT Protocol PDS 15 * 16 * @returns JSX element with work page content 17 */ 18export default function WorkPage(): JSX.Element { 19 const { data: profile, loading, error } = useProtopro(); 20 21 // Split jobs into current and past 22 const currentJobs = profile?.jobHistory.filter((job) => !job.endDate) || []; 23 const pastJobs = 24 profile?.jobHistory 25 .filter((job) => job.endDate) 26 .sort((a, b) => { 27 // Sort by end date, newest first 28 const dateA = a.endDate ? new Date(a.endDate).getTime() : 0; 29 const dateB = b.endDate ? new Date(b.endDate).getTime() : 0; 30 return dateB - dateA; 31 }) || []; 32 33 const jsonLd = { 34 '@context': 'https://schema.org', 35 '@type': 'ProfilePage', 36 mainEntity: { 37 '@type': 'Person', 38 name: 'Barry Prendergast', 39 jobTitle: currentJobs[0]?.position || 'Product Designer', 40 description: 'Independent product designer and strategist', 41 url: 'https://renderg.host/work', 42 }, 43 }; 44 45 return ( 46 <> 47 <Helmet> 48 <title>Work | Barry Prendergast</title> 49 <meta 50 name="description" 51 content="Independent product designer helping organisations deliver better products through clear thinking, practical design, and meaningful collaboration." 52 /> 53 <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} /> 54 </Helmet> 55 56 <Layout theme="default"> 57 <Main> 58 <div className="flex flex-col gap-16"> 59 {/* Heading */} 60 <Section> 61 <Heading level={2} size="base"> 62 work / <Link href="/">renderg.host</Link> 63 </Heading> 64 </Section> 65 66 {/* Loading/Error States */} 67 {loading && ( 68 <Section> 69 <Paragraph size="2xl">Loading profile...</Paragraph> 70 </Section> 71 )} 72 73 {error && ( 74 <Section> 75 <Paragraph size="2xl">Error loading profile: {error}</Paragraph> 76 </Section> 77 )} 78 79 {/* Current Work Section */} 80 {!loading && !error && currentJobs.length > 0 && ( 81 <Section> 82 <Heading level={2} size="lg"> 83 Current roles 84 </Heading> 85 86 <div className="grid grid-cols-1 border-2 border-bones-black-20 dark:border-bones-white-20"> 87 {currentJobs.map((job, index) => ( 88 <> 89 <CardRole key={`current-${job.company}-${index}`} role={job} /> 90 {index < currentJobs.length - 1 && <Divider />} 91 </> 92 ))} 93 </div> 94 </Section> 95 )} 96 97 {/* Past Work Section */} 98 {!loading && !error && pastJobs.length > 0 && ( 99 <Section> 100 <Heading level={2} size="lg"> 101 Previous roles 102 </Heading> 103 104 <div className="grid grid-cols-1 border-2 border-bones-black-20 dark:border-bones-white-20"> 105 {pastJobs.map((job, index) => ( 106 <> 107 <CardRole key={`past-${job.company}-${index}`} role={job} /> 108 {index < pastJobs.length - 1 && <Divider />} 109 </> 110 ))} 111 </div> 112 </Section> 113 )} 114 115 {/* Exit */} 116 <Section> 117 <Paragraph size="md"> 118 Return to <Link href="/">renderg.host</Link> 119 </Paragraph> 120 </Section> 121 </div> 122 </Main> 123 124 <Aside> 125 <TLDRProfile /> 126 </Aside> 127 </Layout> 128 </> 129 ); 130}