@@ -1,11 +0,0 @@ | |||||
export const Person = { | |||||
name: "David Hrdina Němeček", | |||||
brief: "Software developer, people manager.", | |||||
skills: { | |||||
primary: ['Java', 'TypeScript', 'JavaScript'], | |||||
secondary: ['Kotlin', 'Go'], | |||||
others: ['Driver\'s license (B)'] | |||||
}, | |||||
interests: ['Guitars and Heavy Metal', 'Mazda MX-5', 'DIY electronics'], | |||||
}; |
@@ -0,0 +1,29 @@ | |||||
import { PersonalData } from "./PersonalDataTypes"; | |||||
export const personalData: PersonalData = { | |||||
name: "David Hrdina Němeček", | |||||
brief: "Software developer, people manager.", | |||||
jobs: { | |||||
current: { | |||||
position: 'Janitor', | |||||
company: 'Cleaners Limited', | |||||
timerange: '2022 - present', | |||||
description: 'Cleanup duty 24/7.', | |||||
}, | |||||
previous: [ | |||||
{ | |||||
position: 'CEO', | |||||
company: 'CryptoDancers', | |||||
timerange: '2019 - 2022', | |||||
description: 'Revolutionizing the crypto world.', | |||||
} | |||||
] | |||||
}, | |||||
skills: { | |||||
primary: ['Java', 'TypeScript', 'JavaScript'], | |||||
secondary: ['Kotlin', 'Go'], | |||||
others: ['Driver\'s license (B)'] | |||||
}, | |||||
interests: ['Guitars and Heavy Metal', 'Mazda MX-5', 'DIY electronics'], | |||||
}; |
@@ -0,0 +1,25 @@ | |||||
export type Job = { | |||||
position: string, | |||||
company: string, | |||||
timerange: string, | |||||
description: string | |||||
} | |||||
export type Jobs = { | |||||
current?: Job, | |||||
previous?: Job[] | |||||
} | |||||
export type Skills = { | |||||
primary: string[], | |||||
secondary?: string[], | |||||
others?: string[] | |||||
} | |||||
export type PersonalData = { | |||||
name: string, | |||||
brief: string, | |||||
jobs: Jobs, | |||||
skills: Skills, | |||||
interests: string[] | |||||
} |
@@ -52,7 +52,10 @@ export function useAutoFocus<ElementType extends HTMLElement>(elementKey: string | |||||
let cleanup = () => {} | let cleanup = () => {} | ||||
if (elementRef.current) { | if (elementRef.current) { | ||||
const elem = elementRef.current | const elem = elementRef.current | ||||
elem.onclick = focusElement | |||||
elem.onclick = (evt) => { | |||||
evt.stopPropagation() | |||||
focusElement() | |||||
} | |||||
const classNameBackup = elem.className | const classNameBackup = elem.className | ||||
elem.className += ' ' + focusedClass | elem.className += ' ' + focusedClass | ||||
cleanup = () => { | cleanup = () => { | ||||
@@ -1,10 +1,10 @@ | |||||
import React, { useContext } from 'react'; | import React, { useContext } from 'react'; | ||||
import Container from 'react-bootstrap/Container'; | import Container from 'react-bootstrap/Container'; | ||||
import Image from 'react-bootstrap/Image' | import Image from 'react-bootstrap/Image' | ||||
import { PersonContext } from './PersonContext'; | |||||
import { usePersonContext } from './PersonContext'; | |||||
export default function AboutBrief() { | export default function AboutBrief() { | ||||
const person = useContext(PersonContext) | |||||
const person = usePersonContext() | |||||
return ( | return ( | ||||
<Container className='about-brief'> | <Container className='about-brief'> | ||||
<Image alt='Photograph of the person' rounded={true} src='photo.png'></Image> | <Image alt='Photograph of the person' rounded={true} src='photo.png'></Image> | ||||
@@ -11,7 +11,7 @@ export type Props = { | |||||
}; | }; | ||||
export default function JobCard(props: Props) { | export default function JobCard(props: Props) { | ||||
const focusRef = useAutoFocus<HTMLDivElement>([props.position, props.company].join(', ')) | |||||
const focusRef = useAutoFocus<HTMLDivElement>('position ' + [props.position, props.company].join(', ')) | |||||
return ( | return ( | ||||
<Card ref={focusRef} className='job-card'> | <Card ref={focusRef} className='job-card'> | ||||
{props.heading && ( | {props.heading && ( | ||||
@@ -4,35 +4,41 @@ import Container from 'react-bootstrap/Container'; | |||||
import Col from 'react-bootstrap/Col'; | import Col from 'react-bootstrap/Col'; | ||||
import Row from 'react-bootstrap/Row'; | import Row from 'react-bootstrap/Row'; | ||||
import JobCard from './JobCard'; | import JobCard from './JobCard'; | ||||
import { usePersonContext } from './PersonContext'; | |||||
import { Job } from '../../PersonalDataTypes'; | |||||
import { partition } from '../utils'; | |||||
const entriesPerRow = 2 | |||||
export default function JobsHistory() { | export default function JobsHistory() { | ||||
const person = usePersonContext() | |||||
function JobEntry(job: Job, heading?: string) { | |||||
return <JobCard | |||||
heading={heading} | |||||
{...job} | |||||
/> | |||||
} | |||||
return ( | return ( | ||||
<Container> | <Container> | ||||
<h2>Experience</h2> | <h2>Experience</h2> | ||||
{person.jobs.current && ( | |||||
<Row> | <Row> | ||||
<Col > | |||||
<JobCard | |||||
heading='Current position' | |||||
position='Senior Software Development Manager - UI Toolkit' | |||||
timerange='2022 - present' | |||||
company='Oracle | NetSuite' | |||||
description='Team leadership Lorem Ipsum' | |||||
/></Col> | |||||
<Col> | |||||
{JobEntry(person.jobs.current, 'Current position')} | |||||
</Col> | |||||
</Row> | </Row> | ||||
<Row lg={2}> | |||||
<Col><JobCard | |||||
position='Software Development Manager - UI Toolkit' | |||||
timerange='2020 - 2022' | |||||
company='Oracle | NetSuite' | |||||
description='Team leadership Lorem Ipsum' | |||||
/></Col> | |||||
<Col><JobCard | |||||
position='Software Development Manager - ERP Tax' | |||||
timerange='2015 - 2020' | |||||
company='Oracle | NetSuite' | |||||
description='Team leadership Lorem Ipsum' | |||||
/></Col> | |||||
)} | |||||
{partition(person.jobs.previous, entriesPerRow).map((jobs, index) => ( | |||||
<Row key={index}> | |||||
{(jobs.map((job, subindex) => ( | |||||
<Col key={index + '_' + subindex}> | |||||
{JobEntry(job)} | |||||
</Col> | |||||
)))} | |||||
</Row> | </Row> | ||||
</Container> | |||||
))} | |||||
</Container> | |||||
) | ) | ||||
} | } |
@@ -1,14 +1,17 @@ | |||||
'use client' | 'use client' | ||||
import React from 'react'; | |||||
import React, { useContext } from 'react'; | |||||
import { createContext } from 'react'; | import { createContext } from 'react'; | ||||
import { Person } from '../../Person'; | |||||
import { personalData } from '../../PersonalData'; | |||||
import { PersonalData } from '../../PersonalDataTypes'; | |||||
export const PersonContext = createContext(Person); | |||||
export const PersonContext = createContext(personalData); | |||||
export function PersonProvider({ children }) { | export function PersonProvider({ children }) { | ||||
return ( | return ( | ||||
<PersonContext.Provider value={Person}> | |||||
<PersonContext.Provider value={personalData}> | |||||
{children} | {children} | ||||
</PersonContext.Provider> | </PersonContext.Provider> | ||||
) | ) | ||||
} | |||||
} | |||||
export const usePersonContext = (): PersonalData => useContext(PersonContext) |
@@ -9,9 +9,15 @@ export default function Skills() { | |||||
<Container className='skills'> | <Container className='skills'> | ||||
<h2>Skills</h2> | <h2>Skills</h2> | ||||
<TagCloud title='Primary' icon='bookmark-star' style='primary' tags={person.skills.primary} /> | <TagCloud title='Primary' icon='bookmark-star' style='primary' tags={person.skills.primary} /> | ||||
<TagCloud title='Secondary' icon='bookmark-plus' tags={person.skills.secondary} /> | |||||
<TagCloud title='Others' icon='bookmark' tags={person.skills.others} /> | |||||
<TagCloud title='Interests' icon='bookmark-heart' style='light' tags={person.interests} /> | |||||
{person.skills.secondary && ( | |||||
<TagCloud title='Secondary' icon='bookmark-plus' tags={person.skills.secondary} /> | |||||
)} | |||||
{person.skills.others && ( | |||||
<TagCloud title='Others' icon='bookmark' tags={person.skills.others} /> | |||||
)} | |||||
{person.interests && ( | |||||
<TagCloud title='Interests' icon='bookmark-heart' style='light' tags={person.interests} /> | |||||
)} | |||||
</Container> | </Container> | ||||
) | ) | ||||
} | } |
@@ -18,9 +18,10 @@ function Tag(props: {text: string}) { | |||||
} | } | ||||
export default function TagCloud(props: Props) { | export default function TagCloud(props: Props) { | ||||
const focusRef = useAutoFocus<HTMLDivElement>('tags ' + props.title) | |||||
const containerClasses = ['tag-cloud', 'cloud-' + (props.style || 'standard')] | const containerClasses = ['tag-cloud', 'cloud-' + (props.style || 'standard')] | ||||
return ( | return ( | ||||
<Container className={containerClasses.join(' ')}> | |||||
<Container ref={focusRef} className={containerClasses.join(' ')}> | |||||
<h4>{props.icon && (<i className={'bi-' + props.icon}> </i>)}{props.title}</h4> | <h4>{props.icon && (<i className={'bi-' + props.icon}> </i>)}{props.title}</h4> | ||||
<Container className='tag-badges'> | <Container className='tag-badges'> | ||||
{props.tags.map((tag: string) => (<Tag key={tag} text={tag} />) )} | {props.tags.map((tag: string) => (<Tag key={tag} text={tag} />) )} | ||||
@@ -3,11 +3,12 @@ import { Inter } from 'next/font/google' | |||||
import './globals.css' | import './globals.css' | ||||
import { PersonProvider } from './components/PersonContext' | import { PersonProvider } from './components/PersonContext' | ||||
import { personalData } from '../PersonalData' | |||||
const inter = Inter({ subsets: ['latin'] }) | const inter = Inter({ subsets: ['latin'] }) | ||||
export const metadata = { | export const metadata = { | ||||
title: 'David Hrdina Němeček | CV.', | |||||
title: personalData.name + ' | CV.', | |||||
description: 'Curriculum Vitae', | description: 'Curriculum Vitae', | ||||
} | } | ||||
@@ -0,0 +1,10 @@ | |||||
export function partition<T>(array: T[]|undefined, entriesPerRow: number): T[][] { | |||||
return array ? array.reduce((accumulator: T[][], current: T, index) => { | |||||
if (index % entriesPerRow == 0) { | |||||
accumulator[accumulator.length] = [current] | |||||
} else { | |||||
accumulator[accumulator.length - 1] | |||||
} | |||||
return accumulator | |||||
}, []) : [] | |||||
} |