@jsonresume/jsonresume-theme-creative-studio 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +73 -0
- package/package.json +24 -0
- package/src/index.js +84 -0
- package/src/ui/Awards.js +59 -0
- package/src/ui/Certificates.js +66 -0
- package/src/ui/Date.js +11 -0
- package/src/ui/DateRange.js +21 -0
- package/src/ui/Education.js +69 -0
- package/src/ui/Hero.js +115 -0
- package/src/ui/Interests.js +60 -0
- package/src/ui/Languages.js +46 -0
- package/src/ui/Projects.js +84 -0
- package/src/ui/Publications.js +72 -0
- package/src/ui/References.js +44 -0
- package/src/ui/Resume.js +43 -0
- package/src/ui/Section.js +30 -0
- package/src/ui/Skills.js +55 -0
- package/src/ui/Summary.js +20 -0
- package/src/ui/Volunteer.js +49 -0
- package/src/ui/Work.js +67 -0
package/README.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Creative Studio - JSON Resume Theme
|
|
2
|
+
|
|
3
|
+
**Artistic yet professional** resume theme inspired by Behance portfolios. Features clean typography, soft gradient section headers, and creative energy restrained just enough for corporate use while maintaining ATS compatibility.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Rounded Sans-Serif Typography**: Uses Nunito and Poppins for a friendly, modern look
|
|
8
|
+
- **Warm Coral Accent**: Signature #ff6363 color for headers, links, and highlights
|
|
9
|
+
- **Soft Background Blocks**: Light #fff5f5 sections create subtle visual hierarchy
|
|
10
|
+
- **Generous Line Height**: 1.9 line-height for excellent readability
|
|
11
|
+
- **Gradient Hero Section**: Soft gradient background in the header for visual interest
|
|
12
|
+
- **Tag-Style Keywords**: Rounded pill-shaped tags for skills, projects, and interests
|
|
13
|
+
- **Professional Layout**: Single column, 800px max-width, optimized for printing
|
|
14
|
+
|
|
15
|
+
## Design Philosophy
|
|
16
|
+
|
|
17
|
+
Creative Studio strikes the perfect balance between artistic flair and professional presentation. It's designed for creative professionals who want to showcase their personality without sacrificing ATS compatibility or corporate acceptance.
|
|
18
|
+
|
|
19
|
+
## Color Palette
|
|
20
|
+
|
|
21
|
+
- **Accent**: #ff6363 (warm coral) - headers, links, tags
|
|
22
|
+
- **Background**: #fff5f5 (soft pink) - section backgrounds
|
|
23
|
+
- **Text**: #333 (dark gray) - primary text
|
|
24
|
+
- **Secondary**: #555, #666, #999 - hierarchical text levels
|
|
25
|
+
|
|
26
|
+
## Typography
|
|
27
|
+
|
|
28
|
+
- **Headings**: Poppins (weights: 400, 600, 700)
|
|
29
|
+
- **Body**: Nunito (weights: 400, 600, 700)
|
|
30
|
+
- **Line Height**: 1.9 for exceptional readability
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm install @jsonresume/jsonresume-theme-creative-studio
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Usage
|
|
39
|
+
|
|
40
|
+
### With resume-cli
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
resume export resume.html --theme creative-studio
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Programmatically
|
|
47
|
+
|
|
48
|
+
```javascript
|
|
49
|
+
const { render } = require('@jsonresume/jsonresume-theme-creative-studio');
|
|
50
|
+
const resume = require('./resume.json');
|
|
51
|
+
|
|
52
|
+
const html = render(resume);
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Sections Supported
|
|
56
|
+
|
|
57
|
+
- ✅ Basics (name, label, contact, profiles)
|
|
58
|
+
- ✅ Summary
|
|
59
|
+
- ✅ Work Experience
|
|
60
|
+
- ✅ Projects (with keyword tags)
|
|
61
|
+
- ✅ Education
|
|
62
|
+
- ✅ Certificates
|
|
63
|
+
- ✅ Publications
|
|
64
|
+
- ✅ Awards
|
|
65
|
+
- ✅ Volunteer
|
|
66
|
+
- ✅ Languages
|
|
67
|
+
- ✅ Skills (with keyword tags)
|
|
68
|
+
- ✅ Interests (with keyword tags)
|
|
69
|
+
- ✅ References
|
|
70
|
+
|
|
71
|
+
## License
|
|
72
|
+
|
|
73
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"private": false,
|
|
3
|
+
"name": "@jsonresume/jsonresume-theme-creative-studio",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"main": "./src/index.js",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"description": "Creative Studio theme - artistic yet professional with warm coral accents and rounded typography",
|
|
8
|
+
"devDependencies": {
|
|
9
|
+
"react": "^18",
|
|
10
|
+
"styled-components": "^6",
|
|
11
|
+
"@repo/eslint-config-custom": "^0.0.0"
|
|
12
|
+
},
|
|
13
|
+
"peerDependencies": {
|
|
14
|
+
"styled-components": "^6"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"marked": "^16.3.0",
|
|
18
|
+
"prismjs": "^1.29.0",
|
|
19
|
+
"react-icons": "^5.0.1"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"lint": "eslint ."
|
|
23
|
+
}
|
|
24
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { renderToString } from 'react-dom/server';
|
|
2
|
+
import { ServerStyleSheet } from 'styled-components';
|
|
3
|
+
import Resume from './ui/Resume';
|
|
4
|
+
|
|
5
|
+
export const render = (resume) => {
|
|
6
|
+
const sheet = new ServerStyleSheet();
|
|
7
|
+
const html = renderToString(sheet.collectStyles(<Resume resume={resume} />));
|
|
8
|
+
const styles = sheet.getStyleTags();
|
|
9
|
+
return `<!DOCTYPE html><head>
|
|
10
|
+
<title>${resume.basics.name} - Resume</title>
|
|
11
|
+
<meta charset="utf-8">
|
|
12
|
+
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
|
13
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
14
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
15
|
+
<link href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&family=Poppins:wght@400;600;700&display=swap" rel="stylesheet">
|
|
16
|
+
<style>
|
|
17
|
+
html {
|
|
18
|
+
font-family: 'Nunito', 'Poppins', sans-serif;
|
|
19
|
+
background: #fff;
|
|
20
|
+
font-size: 16px;
|
|
21
|
+
color: #333;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
body {
|
|
25
|
+
margin: 0;
|
|
26
|
+
padding: 0;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
h1, h2, h3, h4, h5, h6 {
|
|
30
|
+
font-family: 'Poppins', sans-serif;
|
|
31
|
+
margin: 0;
|
|
32
|
+
padding: 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
h2 {
|
|
36
|
+
font-size: 1.75rem;
|
|
37
|
+
font-weight: 700;
|
|
38
|
+
color: #ff6363;
|
|
39
|
+
margin-bottom: 1rem;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
h3 {
|
|
43
|
+
font-size: 1.25rem;
|
|
44
|
+
font-weight: 600;
|
|
45
|
+
color: #333;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
p {
|
|
49
|
+
padding: 0;
|
|
50
|
+
margin: 0;
|
|
51
|
+
line-height: 1.9;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
p, li {
|
|
55
|
+
font-size: 1rem;
|
|
56
|
+
line-height: 1.9;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
a {
|
|
60
|
+
color: #ff6363;
|
|
61
|
+
text-decoration: none;
|
|
62
|
+
transition: color 0.2s ease;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
a:hover {
|
|
66
|
+
color: #ff4545;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
ul {
|
|
70
|
+
list-style: none;
|
|
71
|
+
margin: 0;
|
|
72
|
+
padding: 0;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
*,
|
|
76
|
+
*::before,
|
|
77
|
+
*::after {
|
|
78
|
+
box-sizing: border-box;
|
|
79
|
+
}
|
|
80
|
+
</style>
|
|
81
|
+
${styles}</head><body>${html}</body></html>`;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export { Resume };
|
package/src/ui/Awards.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
import Section from './Section';
|
|
3
|
+
import Date from './Date';
|
|
4
|
+
|
|
5
|
+
const AwardItem = styled.div`
|
|
6
|
+
margin-bottom: 1.5rem;
|
|
7
|
+
|
|
8
|
+
&:last-child {
|
|
9
|
+
margin-bottom: 0;
|
|
10
|
+
}
|
|
11
|
+
`;
|
|
12
|
+
|
|
13
|
+
const AwardTitle = styled.h3`
|
|
14
|
+
font-size: 1.1rem;
|
|
15
|
+
font-weight: 600;
|
|
16
|
+
color: #333;
|
|
17
|
+
margin-bottom: 0.25rem;
|
|
18
|
+
`;
|
|
19
|
+
|
|
20
|
+
const Awarder = styled.p`
|
|
21
|
+
color: #ff6363;
|
|
22
|
+
font-weight: 600;
|
|
23
|
+
font-size: 0.95rem;
|
|
24
|
+
margin-bottom: 0.25rem;
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
const AwardDate = styled.p`
|
|
28
|
+
color: #999;
|
|
29
|
+
font-size: 0.9rem;
|
|
30
|
+
font-style: italic;
|
|
31
|
+
`;
|
|
32
|
+
|
|
33
|
+
const Summary = styled.p`
|
|
34
|
+
margin-top: 0.5rem;
|
|
35
|
+
color: #555;
|
|
36
|
+
`;
|
|
37
|
+
|
|
38
|
+
const Awards = ({ awards }) => {
|
|
39
|
+
if (!awards || awards.length === 0) return null;
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<Section title="Awards">
|
|
43
|
+
{awards.map((award, i) => (
|
|
44
|
+
<AwardItem key={i}>
|
|
45
|
+
<AwardTitle>{award.title}</AwardTitle>
|
|
46
|
+
{award.awarder && <Awarder>{award.awarder}</Awarder>}
|
|
47
|
+
{award.date && (
|
|
48
|
+
<AwardDate>
|
|
49
|
+
<Date date={award.date} />
|
|
50
|
+
</AwardDate>
|
|
51
|
+
)}
|
|
52
|
+
{award.summary && <Summary>{award.summary}</Summary>}
|
|
53
|
+
</AwardItem>
|
|
54
|
+
))}
|
|
55
|
+
</Section>
|
|
56
|
+
);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export default Awards;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
import Section from './Section';
|
|
3
|
+
import Date from './Date';
|
|
4
|
+
|
|
5
|
+
const CertItem = styled.div`
|
|
6
|
+
margin-bottom: 1.5rem;
|
|
7
|
+
|
|
8
|
+
&:last-child {
|
|
9
|
+
margin-bottom: 0;
|
|
10
|
+
}
|
|
11
|
+
`;
|
|
12
|
+
|
|
13
|
+
const CertName = styled.h3`
|
|
14
|
+
font-size: 1.1rem;
|
|
15
|
+
font-weight: 600;
|
|
16
|
+
color: #333;
|
|
17
|
+
margin-bottom: 0.25rem;
|
|
18
|
+
`;
|
|
19
|
+
|
|
20
|
+
const Issuer = styled.p`
|
|
21
|
+
color: #ff6363;
|
|
22
|
+
font-weight: 600;
|
|
23
|
+
font-size: 0.95rem;
|
|
24
|
+
margin-bottom: 0.25rem;
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
const CertDate = styled.p`
|
|
28
|
+
color: #999;
|
|
29
|
+
font-size: 0.9rem;
|
|
30
|
+
font-style: italic;
|
|
31
|
+
`;
|
|
32
|
+
|
|
33
|
+
const CertLink = styled.a`
|
|
34
|
+
display: inline-block;
|
|
35
|
+
margin-top: 0.5rem;
|
|
36
|
+
color: #ff6363;
|
|
37
|
+
font-weight: 600;
|
|
38
|
+
font-size: 0.9rem;
|
|
39
|
+
`;
|
|
40
|
+
|
|
41
|
+
const Certificates = ({ certificates }) => {
|
|
42
|
+
if (!certificates || certificates.length === 0) return null;
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<Section title="Certificates">
|
|
46
|
+
{certificates.map((cert, i) => (
|
|
47
|
+
<CertItem key={i}>
|
|
48
|
+
<CertName>{cert.name}</CertName>
|
|
49
|
+
{cert.issuer && <Issuer>{cert.issuer}</Issuer>}
|
|
50
|
+
{cert.date && (
|
|
51
|
+
<CertDate>
|
|
52
|
+
<Date date={cert.date} />
|
|
53
|
+
</CertDate>
|
|
54
|
+
)}
|
|
55
|
+
{cert.url && (
|
|
56
|
+
<CertLink href={cert.url} target="_blank" rel="noopener noreferrer">
|
|
57
|
+
View Certificate →
|
|
58
|
+
</CertLink>
|
|
59
|
+
)}
|
|
60
|
+
</CertItem>
|
|
61
|
+
))}
|
|
62
|
+
</Section>
|
|
63
|
+
);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export default Certificates;
|
package/src/ui/Date.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const Date = ({ date }) => {
|
|
2
|
+
if (!date) return null;
|
|
3
|
+
|
|
4
|
+
const dateObj = new window.Date(date);
|
|
5
|
+
const month = dateObj.toLocaleString('default', { month: 'short' });
|
|
6
|
+
const year = dateObj.getFullYear();
|
|
7
|
+
|
|
8
|
+
return `${month} ${year}`;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export default Date;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
import Date from './Date';
|
|
3
|
+
|
|
4
|
+
const DateRangeText = styled.p`
|
|
5
|
+
color: #999;
|
|
6
|
+
font-size: 0.95rem;
|
|
7
|
+
font-style: italic;
|
|
8
|
+
`;
|
|
9
|
+
|
|
10
|
+
const DateRange = ({ startDate, endDate }) => {
|
|
11
|
+
if (!startDate) return null;
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<DateRangeText>
|
|
15
|
+
<Date date={startDate} /> -{' '}
|
|
16
|
+
{endDate ? <Date date={endDate} /> : 'Present'}
|
|
17
|
+
</DateRangeText>
|
|
18
|
+
);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default DateRange;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
import Section from './Section';
|
|
3
|
+
import DateRange from './DateRange';
|
|
4
|
+
|
|
5
|
+
const EducationItem = styled.div`
|
|
6
|
+
margin-bottom: 2rem;
|
|
7
|
+
|
|
8
|
+
&:last-child {
|
|
9
|
+
margin-bottom: 0;
|
|
10
|
+
}
|
|
11
|
+
`;
|
|
12
|
+
|
|
13
|
+
const Degree = styled.h3`
|
|
14
|
+
font-size: 1.25rem;
|
|
15
|
+
font-weight: 600;
|
|
16
|
+
color: #333;
|
|
17
|
+
margin-bottom: 0.25rem;
|
|
18
|
+
`;
|
|
19
|
+
|
|
20
|
+
const Institution = styled.p`
|
|
21
|
+
font-size: 1.1rem;
|
|
22
|
+
color: #ff6363;
|
|
23
|
+
font-weight: 600;
|
|
24
|
+
margin-bottom: 0.5rem;
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
const Area = styled.p`
|
|
28
|
+
color: #666;
|
|
29
|
+
margin-top: 0.25rem;
|
|
30
|
+
`;
|
|
31
|
+
|
|
32
|
+
const Courses = styled.ul`
|
|
33
|
+
margin-top: 0.75rem;
|
|
34
|
+
padding-left: 1.5rem;
|
|
35
|
+
list-style: disc;
|
|
36
|
+
|
|
37
|
+
li {
|
|
38
|
+
color: #555;
|
|
39
|
+
margin-bottom: 0.5rem;
|
|
40
|
+
}
|
|
41
|
+
`;
|
|
42
|
+
|
|
43
|
+
const Education = ({ education }) => {
|
|
44
|
+
if (!education || education.length === 0) return null;
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<Section title="Education">
|
|
48
|
+
{education.map((item, i) => (
|
|
49
|
+
<EducationItem key={i}>
|
|
50
|
+
<Degree>
|
|
51
|
+
{item.studyType} {item.area && `in ${item.area}`}
|
|
52
|
+
</Degree>
|
|
53
|
+
<Institution>{item.institution}</Institution>
|
|
54
|
+
<DateRange startDate={item.startDate} endDate={item.endDate} />
|
|
55
|
+
{item.score && <Area>GPA: {item.score}</Area>}
|
|
56
|
+
{item.courses && item.courses.length > 0 && (
|
|
57
|
+
<Courses>
|
|
58
|
+
{item.courses.map((course, j) => (
|
|
59
|
+
<li key={j}>{course}</li>
|
|
60
|
+
))}
|
|
61
|
+
</Courses>
|
|
62
|
+
)}
|
|
63
|
+
</EducationItem>
|
|
64
|
+
))}
|
|
65
|
+
</Section>
|
|
66
|
+
);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export default Education;
|
package/src/ui/Hero.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
import { FaEnvelope, FaPhone, FaGlobe, FaMapMarkerAlt } from 'react-icons/fa';
|
|
3
|
+
|
|
4
|
+
const HeroContainer = styled.div`
|
|
5
|
+
background: linear-gradient(135deg, #fff5f5 0%, #ffe8e8 100%);
|
|
6
|
+
padding: 40px;
|
|
7
|
+
border-radius: 12px;
|
|
8
|
+
margin-bottom: 40px;
|
|
9
|
+
text-align: center;
|
|
10
|
+
`;
|
|
11
|
+
|
|
12
|
+
const Name = styled.h1`
|
|
13
|
+
font-size: 3rem;
|
|
14
|
+
font-weight: 700;
|
|
15
|
+
color: #333;
|
|
16
|
+
margin-bottom: 0.5rem;
|
|
17
|
+
line-height: 1.2;
|
|
18
|
+
`;
|
|
19
|
+
|
|
20
|
+
const Label = styled.p`
|
|
21
|
+
font-size: 1.25rem;
|
|
22
|
+
color: #ff6363;
|
|
23
|
+
font-weight: 600;
|
|
24
|
+
margin-bottom: 1.5rem;
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
const ContactInfo = styled.div`
|
|
28
|
+
display: flex;
|
|
29
|
+
flex-wrap: wrap;
|
|
30
|
+
justify-content: center;
|
|
31
|
+
gap: 20px;
|
|
32
|
+
margin-top: 20px;
|
|
33
|
+
`;
|
|
34
|
+
|
|
35
|
+
const ContactItem = styled.a`
|
|
36
|
+
display: flex;
|
|
37
|
+
align-items: center;
|
|
38
|
+
gap: 8px;
|
|
39
|
+
color: #666;
|
|
40
|
+
font-size: 0.95rem;
|
|
41
|
+
|
|
42
|
+
svg {
|
|
43
|
+
color: #ff6363;
|
|
44
|
+
}
|
|
45
|
+
`;
|
|
46
|
+
|
|
47
|
+
const Profiles = styled.div`
|
|
48
|
+
display: flex;
|
|
49
|
+
flex-wrap: wrap;
|
|
50
|
+
justify-content: center;
|
|
51
|
+
gap: 15px;
|
|
52
|
+
margin-top: 20px;
|
|
53
|
+
`;
|
|
54
|
+
|
|
55
|
+
const ProfileLink = styled.a`
|
|
56
|
+
color: #ff6363;
|
|
57
|
+
font-weight: 600;
|
|
58
|
+
font-size: 0.95rem;
|
|
59
|
+
`;
|
|
60
|
+
|
|
61
|
+
const Hero = ({ basics }) => {
|
|
62
|
+
if (!basics) return null;
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<HeroContainer>
|
|
66
|
+
<Name>{basics.name}</Name>
|
|
67
|
+
{basics.label && <Label>{basics.label}</Label>}
|
|
68
|
+
|
|
69
|
+
<ContactInfo>
|
|
70
|
+
{basics.email && (
|
|
71
|
+
<ContactItem href={`mailto:${basics.email}`}>
|
|
72
|
+
<FaEnvelope /> {basics.email}
|
|
73
|
+
</ContactItem>
|
|
74
|
+
)}
|
|
75
|
+
{basics.phone && (
|
|
76
|
+
<ContactItem href={`tel:${basics.phone}`}>
|
|
77
|
+
<FaPhone /> {basics.phone}
|
|
78
|
+
</ContactItem>
|
|
79
|
+
)}
|
|
80
|
+
{basics.url && (
|
|
81
|
+
<ContactItem
|
|
82
|
+
href={basics.url}
|
|
83
|
+
target="_blank"
|
|
84
|
+
rel="noopener noreferrer"
|
|
85
|
+
>
|
|
86
|
+
<FaGlobe /> {basics.url.replace(/^https?:\/\//, '')}
|
|
87
|
+
</ContactItem>
|
|
88
|
+
)}
|
|
89
|
+
{basics.location?.city && (
|
|
90
|
+
<ContactItem as="span">
|
|
91
|
+
<FaMapMarkerAlt /> {basics.location.city}
|
|
92
|
+
{basics.location.region && `, ${basics.location.region}`}
|
|
93
|
+
</ContactItem>
|
|
94
|
+
)}
|
|
95
|
+
</ContactInfo>
|
|
96
|
+
|
|
97
|
+
{basics.profiles && basics.profiles.length > 0 && (
|
|
98
|
+
<Profiles>
|
|
99
|
+
{basics.profiles.map((profile, i) => (
|
|
100
|
+
<ProfileLink
|
|
101
|
+
key={i}
|
|
102
|
+
href={profile.url}
|
|
103
|
+
target="_blank"
|
|
104
|
+
rel="noopener noreferrer"
|
|
105
|
+
>
|
|
106
|
+
{profile.network}
|
|
107
|
+
</ProfileLink>
|
|
108
|
+
))}
|
|
109
|
+
</Profiles>
|
|
110
|
+
)}
|
|
111
|
+
</HeroContainer>
|
|
112
|
+
);
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export default Hero;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
import Section from './Section';
|
|
3
|
+
|
|
4
|
+
const InterestList = styled.div`
|
|
5
|
+
display: flex;
|
|
6
|
+
flex-wrap: wrap;
|
|
7
|
+
gap: 1rem;
|
|
8
|
+
`;
|
|
9
|
+
|
|
10
|
+
const InterestItem = styled.div`
|
|
11
|
+
background: #ffe8e8;
|
|
12
|
+
padding: 15px 20px;
|
|
13
|
+
border-radius: 20px;
|
|
14
|
+
`;
|
|
15
|
+
|
|
16
|
+
const InterestName = styled.h3`
|
|
17
|
+
font-size: 1rem;
|
|
18
|
+
font-weight: 600;
|
|
19
|
+
color: #333;
|
|
20
|
+
margin-bottom: 0.5rem;
|
|
21
|
+
`;
|
|
22
|
+
|
|
23
|
+
const Keywords = styled.div`
|
|
24
|
+
display: flex;
|
|
25
|
+
flex-wrap: wrap;
|
|
26
|
+
gap: 6px;
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
const Keyword = styled.span`
|
|
30
|
+
background: #fff;
|
|
31
|
+
color: #ff6363;
|
|
32
|
+
padding: 3px 10px;
|
|
33
|
+
border-radius: 12px;
|
|
34
|
+
font-size: 0.85rem;
|
|
35
|
+
`;
|
|
36
|
+
|
|
37
|
+
const Interests = ({ interests }) => {
|
|
38
|
+
if (!interests || interests.length === 0) return null;
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<Section title="Interests">
|
|
42
|
+
<InterestList>
|
|
43
|
+
{interests.map((interest, i) => (
|
|
44
|
+
<InterestItem key={i}>
|
|
45
|
+
<InterestName>{interest.name}</InterestName>
|
|
46
|
+
{interest.keywords && interest.keywords.length > 0 && (
|
|
47
|
+
<Keywords>
|
|
48
|
+
{interest.keywords.map((keyword, j) => (
|
|
49
|
+
<Keyword key={j}>{keyword}</Keyword>
|
|
50
|
+
))}
|
|
51
|
+
</Keywords>
|
|
52
|
+
)}
|
|
53
|
+
</InterestItem>
|
|
54
|
+
))}
|
|
55
|
+
</InterestList>
|
|
56
|
+
</Section>
|
|
57
|
+
);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export default Interests;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
import Section from './Section';
|
|
3
|
+
|
|
4
|
+
const LanguageList = styled.div`
|
|
5
|
+
display: grid;
|
|
6
|
+
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
|
7
|
+
gap: 1rem;
|
|
8
|
+
`;
|
|
9
|
+
|
|
10
|
+
const LanguageItem = styled.div`
|
|
11
|
+
background: #ffe8e8;
|
|
12
|
+
padding: 15px;
|
|
13
|
+
border-radius: 8px;
|
|
14
|
+
`;
|
|
15
|
+
|
|
16
|
+
const LanguageName = styled.h3`
|
|
17
|
+
font-size: 1.1rem;
|
|
18
|
+
font-weight: 600;
|
|
19
|
+
color: #333;
|
|
20
|
+
margin-bottom: 0.25rem;
|
|
21
|
+
`;
|
|
22
|
+
|
|
23
|
+
const Fluency = styled.p`
|
|
24
|
+
color: #ff6363;
|
|
25
|
+
font-weight: 600;
|
|
26
|
+
font-size: 0.9rem;
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
const Languages = ({ languages }) => {
|
|
30
|
+
if (!languages || languages.length === 0) return null;
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<Section title="Languages">
|
|
34
|
+
<LanguageList>
|
|
35
|
+
{languages.map((lang, i) => (
|
|
36
|
+
<LanguageItem key={i}>
|
|
37
|
+
<LanguageName>{lang.language}</LanguageName>
|
|
38
|
+
{lang.fluency && <Fluency>{lang.fluency}</Fluency>}
|
|
39
|
+
</LanguageItem>
|
|
40
|
+
))}
|
|
41
|
+
</LanguageList>
|
|
42
|
+
</Section>
|
|
43
|
+
);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export default Languages;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
import Section from './Section';
|
|
3
|
+
import DateRange from './DateRange';
|
|
4
|
+
|
|
5
|
+
const ProjectItem = styled.div`
|
|
6
|
+
margin-bottom: 2rem;
|
|
7
|
+
|
|
8
|
+
&:last-child {
|
|
9
|
+
margin-bottom: 0;
|
|
10
|
+
}
|
|
11
|
+
`;
|
|
12
|
+
|
|
13
|
+
const ProjectName = styled.h3`
|
|
14
|
+
font-size: 1.25rem;
|
|
15
|
+
font-weight: 600;
|
|
16
|
+
color: #333;
|
|
17
|
+
margin-bottom: 0.25rem;
|
|
18
|
+
`;
|
|
19
|
+
|
|
20
|
+
const ProjectLink = styled.a`
|
|
21
|
+
color: #ff6363;
|
|
22
|
+
font-weight: 600;
|
|
23
|
+
margin-left: 0.5rem;
|
|
24
|
+
font-size: 0.95rem;
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
const Description = styled.p`
|
|
28
|
+
margin-top: 0.75rem;
|
|
29
|
+
color: #555;
|
|
30
|
+
`;
|
|
31
|
+
|
|
32
|
+
const Keywords = styled.div`
|
|
33
|
+
display: flex;
|
|
34
|
+
flex-wrap: wrap;
|
|
35
|
+
gap: 8px;
|
|
36
|
+
margin-top: 0.75rem;
|
|
37
|
+
`;
|
|
38
|
+
|
|
39
|
+
const Keyword = styled.span`
|
|
40
|
+
background: #ffe8e8;
|
|
41
|
+
color: #ff6363;
|
|
42
|
+
padding: 4px 12px;
|
|
43
|
+
border-radius: 16px;
|
|
44
|
+
font-size: 0.85rem;
|
|
45
|
+
font-weight: 600;
|
|
46
|
+
`;
|
|
47
|
+
|
|
48
|
+
const Projects = ({ projects }) => {
|
|
49
|
+
if (!projects || projects.length === 0) return null;
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<Section title="Projects">
|
|
53
|
+
{projects.map((project, i) => (
|
|
54
|
+
<ProjectItem key={i}>
|
|
55
|
+
<ProjectName>
|
|
56
|
+
{project.name}
|
|
57
|
+
{project.url && (
|
|
58
|
+
<ProjectLink
|
|
59
|
+
href={project.url}
|
|
60
|
+
target="_blank"
|
|
61
|
+
rel="noopener noreferrer"
|
|
62
|
+
>
|
|
63
|
+
View Project →
|
|
64
|
+
</ProjectLink>
|
|
65
|
+
)}
|
|
66
|
+
</ProjectName>
|
|
67
|
+
<DateRange startDate={project.startDate} endDate={project.endDate} />
|
|
68
|
+
{project.description && (
|
|
69
|
+
<Description>{project.description}</Description>
|
|
70
|
+
)}
|
|
71
|
+
{project.keywords && project.keywords.length > 0 && (
|
|
72
|
+
<Keywords>
|
|
73
|
+
{project.keywords.map((keyword, j) => (
|
|
74
|
+
<Keyword key={j}>{keyword}</Keyword>
|
|
75
|
+
))}
|
|
76
|
+
</Keywords>
|
|
77
|
+
)}
|
|
78
|
+
</ProjectItem>
|
|
79
|
+
))}
|
|
80
|
+
</Section>
|
|
81
|
+
);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export default Projects;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
import Section from './Section';
|
|
3
|
+
import Date from './Date';
|
|
4
|
+
|
|
5
|
+
const PubItem = styled.div`
|
|
6
|
+
margin-bottom: 1.5rem;
|
|
7
|
+
|
|
8
|
+
&:last-child {
|
|
9
|
+
margin-bottom: 0;
|
|
10
|
+
}
|
|
11
|
+
`;
|
|
12
|
+
|
|
13
|
+
const PubName = styled.h3`
|
|
14
|
+
font-size: 1.1rem;
|
|
15
|
+
font-weight: 600;
|
|
16
|
+
color: #333;
|
|
17
|
+
margin-bottom: 0.25rem;
|
|
18
|
+
`;
|
|
19
|
+
|
|
20
|
+
const Publisher = styled.p`
|
|
21
|
+
color: #ff6363;
|
|
22
|
+
font-weight: 600;
|
|
23
|
+
font-size: 0.95rem;
|
|
24
|
+
margin-bottom: 0.25rem;
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
const PubDate = styled.p`
|
|
28
|
+
color: #999;
|
|
29
|
+
font-size: 0.9rem;
|
|
30
|
+
font-style: italic;
|
|
31
|
+
`;
|
|
32
|
+
|
|
33
|
+
const Summary = styled.p`
|
|
34
|
+
margin-top: 0.5rem;
|
|
35
|
+
color: #555;
|
|
36
|
+
`;
|
|
37
|
+
|
|
38
|
+
const PubLink = styled.a`
|
|
39
|
+
display: inline-block;
|
|
40
|
+
margin-top: 0.5rem;
|
|
41
|
+
color: #ff6363;
|
|
42
|
+
font-weight: 600;
|
|
43
|
+
font-size: 0.9rem;
|
|
44
|
+
`;
|
|
45
|
+
|
|
46
|
+
const Publications = ({ publications }) => {
|
|
47
|
+
if (!publications || publications.length === 0) return null;
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<Section title="Publications">
|
|
51
|
+
{publications.map((pub, i) => (
|
|
52
|
+
<PubItem key={i}>
|
|
53
|
+
<PubName>{pub.name}</PubName>
|
|
54
|
+
{pub.publisher && <Publisher>{pub.publisher}</Publisher>}
|
|
55
|
+
{pub.releaseDate && (
|
|
56
|
+
<PubDate>
|
|
57
|
+
<Date date={pub.releaseDate} />
|
|
58
|
+
</PubDate>
|
|
59
|
+
)}
|
|
60
|
+
{pub.summary && <Summary>{pub.summary}</Summary>}
|
|
61
|
+
{pub.url && (
|
|
62
|
+
<PubLink href={pub.url} target="_blank" rel="noopener noreferrer">
|
|
63
|
+
Read Publication →
|
|
64
|
+
</PubLink>
|
|
65
|
+
)}
|
|
66
|
+
</PubItem>
|
|
67
|
+
))}
|
|
68
|
+
</Section>
|
|
69
|
+
);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export default Publications;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
import Section from './Section';
|
|
3
|
+
|
|
4
|
+
const ReferenceItem = styled.div`
|
|
5
|
+
margin-bottom: 1.5rem;
|
|
6
|
+
background: #ffe8e8;
|
|
7
|
+
padding: 20px;
|
|
8
|
+
border-radius: 8px;
|
|
9
|
+
border-left: 4px solid #ff6363;
|
|
10
|
+
|
|
11
|
+
&:last-child {
|
|
12
|
+
margin-bottom: 0;
|
|
13
|
+
}
|
|
14
|
+
`;
|
|
15
|
+
|
|
16
|
+
const RefName = styled.h3`
|
|
17
|
+
font-size: 1.1rem;
|
|
18
|
+
font-weight: 600;
|
|
19
|
+
color: #333;
|
|
20
|
+
margin-bottom: 0.5rem;
|
|
21
|
+
`;
|
|
22
|
+
|
|
23
|
+
const Quote = styled.p`
|
|
24
|
+
color: #555;
|
|
25
|
+
font-style: italic;
|
|
26
|
+
line-height: 1.9;
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
const References = ({ references }) => {
|
|
30
|
+
if (!references || references.length === 0) return null;
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<Section title="References">
|
|
34
|
+
{references.map((ref, i) => (
|
|
35
|
+
<ReferenceItem key={i}>
|
|
36
|
+
<RefName>{ref.name}</RefName>
|
|
37
|
+
{ref.reference && <Quote>"{ref.reference}"</Quote>}
|
|
38
|
+
</ReferenceItem>
|
|
39
|
+
))}
|
|
40
|
+
</Section>
|
|
41
|
+
);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export default References;
|
package/src/ui/Resume.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
import Projects from './Projects';
|
|
3
|
+
import Hero from './Hero';
|
|
4
|
+
import Summary from './Summary';
|
|
5
|
+
import Education from './Education';
|
|
6
|
+
import Work from './Work';
|
|
7
|
+
import Certificates from './Certificates';
|
|
8
|
+
import Publications from './Publications';
|
|
9
|
+
import Awards from './Awards';
|
|
10
|
+
import Skills from './Skills';
|
|
11
|
+
import Interests from './Interests';
|
|
12
|
+
import Languages from './Languages';
|
|
13
|
+
import References from './References';
|
|
14
|
+
import Volunteer from './Volunteer';
|
|
15
|
+
|
|
16
|
+
const Layout = styled.div`
|
|
17
|
+
max-width: 800px;
|
|
18
|
+
margin: 0 auto;
|
|
19
|
+
padding: 40px 20px;
|
|
20
|
+
line-height: 1.9;
|
|
21
|
+
`;
|
|
22
|
+
|
|
23
|
+
const Resume = ({ resume }) => {
|
|
24
|
+
return (
|
|
25
|
+
<Layout>
|
|
26
|
+
<Hero basics={resume.basics} />
|
|
27
|
+
<Summary basics={resume.basics} />
|
|
28
|
+
<Work work={resume.work} />
|
|
29
|
+
<Projects projects={resume.projects} />
|
|
30
|
+
<Education education={resume.education} />
|
|
31
|
+
<Certificates certificates={resume.certificates} />
|
|
32
|
+
<Publications publications={resume.publications} />
|
|
33
|
+
<Awards awards={resume.awards} />
|
|
34
|
+
<Volunteer volunteer={resume.volunteer} />
|
|
35
|
+
<Languages languages={resume.languages} />
|
|
36
|
+
<Skills skills={resume.skills} />
|
|
37
|
+
<Interests interests={resume.interests} />
|
|
38
|
+
<References references={resume.references} />
|
|
39
|
+
</Layout>
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export default Resume;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
|
|
3
|
+
const SectionContainer = styled.section`
|
|
4
|
+
background: #fff5f5;
|
|
5
|
+
padding: 30px;
|
|
6
|
+
border-radius: 12px;
|
|
7
|
+
margin-bottom: 30px;
|
|
8
|
+
`;
|
|
9
|
+
|
|
10
|
+
const SectionTitle = styled.h2`
|
|
11
|
+
font-size: 1.75rem;
|
|
12
|
+
font-weight: 700;
|
|
13
|
+
color: #ff6363;
|
|
14
|
+
margin-bottom: 1.5rem;
|
|
15
|
+
padding-bottom: 0.5rem;
|
|
16
|
+
border-bottom: 2px solid #ff6363;
|
|
17
|
+
`;
|
|
18
|
+
|
|
19
|
+
const Section = ({ title, children }) => {
|
|
20
|
+
if (!children) return null;
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<SectionContainer>
|
|
24
|
+
<SectionTitle>{title}</SectionTitle>
|
|
25
|
+
{children}
|
|
26
|
+
</SectionContainer>
|
|
27
|
+
);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export default Section;
|
package/src/ui/Skills.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
import Section from './Section';
|
|
3
|
+
|
|
4
|
+
const SkillCategory = styled.div`
|
|
5
|
+
margin-bottom: 1.5rem;
|
|
6
|
+
|
|
7
|
+
&:last-child {
|
|
8
|
+
margin-bottom: 0;
|
|
9
|
+
}
|
|
10
|
+
`;
|
|
11
|
+
|
|
12
|
+
const SkillName = styled.h3`
|
|
13
|
+
font-size: 1.1rem;
|
|
14
|
+
font-weight: 600;
|
|
15
|
+
color: #333;
|
|
16
|
+
margin-bottom: 0.5rem;
|
|
17
|
+
`;
|
|
18
|
+
|
|
19
|
+
const SkillList = styled.div`
|
|
20
|
+
display: flex;
|
|
21
|
+
flex-wrap: wrap;
|
|
22
|
+
gap: 8px;
|
|
23
|
+
`;
|
|
24
|
+
|
|
25
|
+
const SkillItem = styled.span`
|
|
26
|
+
background: #ffe8e8;
|
|
27
|
+
color: #ff6363;
|
|
28
|
+
padding: 6px 14px;
|
|
29
|
+
border-radius: 16px;
|
|
30
|
+
font-size: 0.9rem;
|
|
31
|
+
font-weight: 600;
|
|
32
|
+
`;
|
|
33
|
+
|
|
34
|
+
const Skills = ({ skills }) => {
|
|
35
|
+
if (!skills || skills.length === 0) return null;
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<Section title="Skills">
|
|
39
|
+
{skills.map((skill, i) => (
|
|
40
|
+
<SkillCategory key={i}>
|
|
41
|
+
<SkillName>{skill.name}</SkillName>
|
|
42
|
+
{skill.keywords && skill.keywords.length > 0 && (
|
|
43
|
+
<SkillList>
|
|
44
|
+
{skill.keywords.map((keyword, j) => (
|
|
45
|
+
<SkillItem key={j}>{keyword}</SkillItem>
|
|
46
|
+
))}
|
|
47
|
+
</SkillList>
|
|
48
|
+
)}
|
|
49
|
+
</SkillCategory>
|
|
50
|
+
))}
|
|
51
|
+
</Section>
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export default Skills;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
import Section from './Section';
|
|
3
|
+
|
|
4
|
+
const SummaryText = styled.p`
|
|
5
|
+
font-size: 1.1rem;
|
|
6
|
+
line-height: 1.9;
|
|
7
|
+
color: #555;
|
|
8
|
+
`;
|
|
9
|
+
|
|
10
|
+
const Summary = ({ basics }) => {
|
|
11
|
+
if (!basics?.summary) return null;
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<Section title="About">
|
|
15
|
+
<SummaryText>{basics.summary}</SummaryText>
|
|
16
|
+
</Section>
|
|
17
|
+
);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export default Summary;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
import Section from './Section';
|
|
3
|
+
import DateRange from './DateRange';
|
|
4
|
+
|
|
5
|
+
const VolunteerItem = styled.div`
|
|
6
|
+
margin-bottom: 2rem;
|
|
7
|
+
|
|
8
|
+
&:last-child {
|
|
9
|
+
margin-bottom: 0;
|
|
10
|
+
}
|
|
11
|
+
`;
|
|
12
|
+
|
|
13
|
+
const Position = styled.h3`
|
|
14
|
+
font-size: 1.25rem;
|
|
15
|
+
font-weight: 600;
|
|
16
|
+
color: #333;
|
|
17
|
+
margin-bottom: 0.25rem;
|
|
18
|
+
`;
|
|
19
|
+
|
|
20
|
+
const Organization = styled.p`
|
|
21
|
+
font-size: 1.1rem;
|
|
22
|
+
color: #ff6363;
|
|
23
|
+
font-weight: 600;
|
|
24
|
+
margin-bottom: 0.5rem;
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
const Description = styled.p`
|
|
28
|
+
margin-top: 0.75rem;
|
|
29
|
+
color: #555;
|
|
30
|
+
`;
|
|
31
|
+
|
|
32
|
+
const Volunteer = ({ volunteer }) => {
|
|
33
|
+
if (!volunteer || volunteer.length === 0) return null;
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<Section title="Volunteer">
|
|
37
|
+
{volunteer.map((item, i) => (
|
|
38
|
+
<VolunteerItem key={i}>
|
|
39
|
+
<Position>{item.position}</Position>
|
|
40
|
+
<Organization>{item.organization}</Organization>
|
|
41
|
+
<DateRange startDate={item.startDate} endDate={item.endDate} />
|
|
42
|
+
{item.summary && <Description>{item.summary}</Description>}
|
|
43
|
+
</VolunteerItem>
|
|
44
|
+
))}
|
|
45
|
+
</Section>
|
|
46
|
+
);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export default Volunteer;
|
package/src/ui/Work.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
import Section from './Section';
|
|
3
|
+
import DateRange from './DateRange';
|
|
4
|
+
|
|
5
|
+
const WorkItem = styled.div`
|
|
6
|
+
margin-bottom: 2rem;
|
|
7
|
+
|
|
8
|
+
&:last-child {
|
|
9
|
+
margin-bottom: 0;
|
|
10
|
+
}
|
|
11
|
+
`;
|
|
12
|
+
|
|
13
|
+
const Position = styled.h3`
|
|
14
|
+
font-size: 1.25rem;
|
|
15
|
+
font-weight: 600;
|
|
16
|
+
color: #333;
|
|
17
|
+
margin-bottom: 0.25rem;
|
|
18
|
+
`;
|
|
19
|
+
|
|
20
|
+
const Company = styled.p`
|
|
21
|
+
font-size: 1.1rem;
|
|
22
|
+
color: #ff6363;
|
|
23
|
+
font-weight: 600;
|
|
24
|
+
margin-bottom: 0.5rem;
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
const Description = styled.p`
|
|
28
|
+
margin-top: 0.75rem;
|
|
29
|
+
color: #555;
|
|
30
|
+
`;
|
|
31
|
+
|
|
32
|
+
const Highlights = styled.ul`
|
|
33
|
+
margin-top: 0.75rem;
|
|
34
|
+
padding-left: 1.5rem;
|
|
35
|
+
list-style: disc;
|
|
36
|
+
|
|
37
|
+
li {
|
|
38
|
+
color: #555;
|
|
39
|
+
margin-bottom: 0.5rem;
|
|
40
|
+
}
|
|
41
|
+
`;
|
|
42
|
+
|
|
43
|
+
const Work = ({ work }) => {
|
|
44
|
+
if (!work || work.length === 0) return null;
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<Section title="Experience">
|
|
48
|
+
{work.map((item, i) => (
|
|
49
|
+
<WorkItem key={i}>
|
|
50
|
+
<Position>{item.position}</Position>
|
|
51
|
+
<Company>{item.name}</Company>
|
|
52
|
+
<DateRange startDate={item.startDate} endDate={item.endDate} />
|
|
53
|
+
{item.summary && <Description>{item.summary}</Description>}
|
|
54
|
+
{item.highlights && item.highlights.length > 0 && (
|
|
55
|
+
<Highlights>
|
|
56
|
+
{item.highlights.map((highlight, j) => (
|
|
57
|
+
<li key={j}>{highlight}</li>
|
|
58
|
+
))}
|
|
59
|
+
</Highlights>
|
|
60
|
+
)}
|
|
61
|
+
</WorkItem>
|
|
62
|
+
))}
|
|
63
|
+
</Section>
|
|
64
|
+
);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export default Work;
|