jsonresume-theme-university-first 0.1.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 +42 -0
- package/package.json +25 -0
- package/src/Resume.jsx +562 -0
- package/src/index.js +53 -0
package/README.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# JSON Resume Theme - University First
|
|
2
|
+
|
|
3
|
+
**For fresh grads - education credentials come first, work second**
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
A professional, student-oriented resume theme designed for recent graduates and early-career applicants. This theme emphasizes educational achievements by placing the Education section first, followed by Skills, Projects, and then Work Experience.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **Education-First Layout**: Your academic credentials take center stage
|
|
12
|
+
- **Clean Typography**: Humanist sans-serif (Inter) at comfortable 11pt body text
|
|
13
|
+
- **School Blue Accent**: Professional #2563eb blue throughout
|
|
14
|
+
- **Scholarly Touch**: Serif fonts for institution names to emphasize academic credentials
|
|
15
|
+
- **White Background**: Clean, professional appearance
|
|
16
|
+
- **Single Column**: Easy to read and ATS-friendly
|
|
17
|
+
- **Comprehensive Sections**: Supports all 12 JSON Resume sections
|
|
18
|
+
|
|
19
|
+
## Section Order
|
|
20
|
+
|
|
21
|
+
1. Education
|
|
22
|
+
2. Skills
|
|
23
|
+
3. Projects
|
|
24
|
+
4. Work Experience
|
|
25
|
+
5. Volunteer Experience
|
|
26
|
+
6. Awards & Honors
|
|
27
|
+
7. Publications
|
|
28
|
+
8. Languages
|
|
29
|
+
9. Interests
|
|
30
|
+
10. References
|
|
31
|
+
|
|
32
|
+
## Design Philosophy
|
|
33
|
+
|
|
34
|
+
Lightweight and approachable design with bright blue accents and generous spacing. Balances youthful energy with professional readability - perfect for showcasing your academic journey while maintaining a polished, career-ready presentation.
|
|
35
|
+
|
|
36
|
+
## Usage
|
|
37
|
+
|
|
38
|
+
This theme is part of the JSON Resume ecosystem and will be automatically available through the jsonresume.org platform.
|
|
39
|
+
|
|
40
|
+
## License
|
|
41
|
+
|
|
42
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "jsonresume-theme-university-first",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "For fresh grads - education credentials come first, work second",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"jsonresume",
|
|
10
|
+
"theme",
|
|
11
|
+
"student",
|
|
12
|
+
"graduate",
|
|
13
|
+
"education",
|
|
14
|
+
"university",
|
|
15
|
+
"fresh-grad"
|
|
16
|
+
],
|
|
17
|
+
"peerDependencies": {
|
|
18
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
19
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"styled-components": "^6.1.19",
|
|
23
|
+
"@resume/core": "0.1.0"
|
|
24
|
+
}
|
|
25
|
+
}
|
package/src/Resume.jsx
ADDED
|
@@ -0,0 +1,562 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
import { Section, SectionTitle, DateRange, ContactInfo } from '@resume/core';
|
|
4
|
+
|
|
5
|
+
const Layout = styled.div`
|
|
6
|
+
max-width: 900px;
|
|
7
|
+
margin: 0 auto;
|
|
8
|
+
padding: 0;
|
|
9
|
+
background: linear-gradient(to bottom, #dbeafe 0%, #ffffff 300px);
|
|
10
|
+
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI',
|
|
11
|
+
sans-serif;
|
|
12
|
+
color: #1f2937;
|
|
13
|
+
font-size: 11pt;
|
|
14
|
+
line-height: 1.6;
|
|
15
|
+
min-height: 100vh;
|
|
16
|
+
|
|
17
|
+
@media print {
|
|
18
|
+
background: white;
|
|
19
|
+
min-height: auto;
|
|
20
|
+
}
|
|
21
|
+
`;
|
|
22
|
+
|
|
23
|
+
const Header = styled.header`
|
|
24
|
+
background: #2563eb;
|
|
25
|
+
color: white;
|
|
26
|
+
padding: 60px 50px;
|
|
27
|
+
text-align: center;
|
|
28
|
+
position: relative;
|
|
29
|
+
|
|
30
|
+
&::after {
|
|
31
|
+
content: '';
|
|
32
|
+
position: absolute;
|
|
33
|
+
bottom: -30px;
|
|
34
|
+
left: 50%;
|
|
35
|
+
transform: translateX(-50%);
|
|
36
|
+
width: 0;
|
|
37
|
+
height: 0;
|
|
38
|
+
border-left: 30px solid transparent;
|
|
39
|
+
border-right: 30px solid transparent;
|
|
40
|
+
border-top: 30px solid #2563eb;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@media print {
|
|
44
|
+
padding: 40px;
|
|
45
|
+
&::after {
|
|
46
|
+
display: none;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
`;
|
|
50
|
+
|
|
51
|
+
const Name = styled.h1`
|
|
52
|
+
font-size: 48px;
|
|
53
|
+
font-weight: 800;
|
|
54
|
+
color: white;
|
|
55
|
+
margin: 0 0 12px 0;
|
|
56
|
+
letter-spacing: -0.5px;
|
|
57
|
+
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
58
|
+
`;
|
|
59
|
+
|
|
60
|
+
const Label = styled.div`
|
|
61
|
+
font-size: 18px;
|
|
62
|
+
color: #dbeafe;
|
|
63
|
+
margin-bottom: 20px;
|
|
64
|
+
font-weight: 600;
|
|
65
|
+
letter-spacing: 0.3px;
|
|
66
|
+
`;
|
|
67
|
+
|
|
68
|
+
const StyledContactInfo = styled(ContactInfo)`
|
|
69
|
+
font-size: 15px;
|
|
70
|
+
justify-content: center;
|
|
71
|
+
color: #dbeafe;
|
|
72
|
+
|
|
73
|
+
a {
|
|
74
|
+
font-size: 15px;
|
|
75
|
+
color: white;
|
|
76
|
+
text-decoration: none;
|
|
77
|
+
|
|
78
|
+
&:hover {
|
|
79
|
+
text-decoration: underline;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
`;
|
|
83
|
+
|
|
84
|
+
const Content = styled.div`
|
|
85
|
+
padding: 60px 50px;
|
|
86
|
+
|
|
87
|
+
@media print {
|
|
88
|
+
padding: 40px;
|
|
89
|
+
}
|
|
90
|
+
`;
|
|
91
|
+
|
|
92
|
+
const Summary = styled.p`
|
|
93
|
+
font-size: 15px;
|
|
94
|
+
line-height: 1.8;
|
|
95
|
+
color: #475569;
|
|
96
|
+
margin: 0 0 40px 0;
|
|
97
|
+
text-align: center;
|
|
98
|
+
max-width: 700px;
|
|
99
|
+
margin-left: auto;
|
|
100
|
+
margin-right: auto;
|
|
101
|
+
background: white;
|
|
102
|
+
padding: 30px;
|
|
103
|
+
border-radius: 12px;
|
|
104
|
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
|
|
105
|
+
`;
|
|
106
|
+
|
|
107
|
+
const StyledSectionTitle = styled(SectionTitle)`
|
|
108
|
+
font-size: 22px;
|
|
109
|
+
font-weight: 700;
|
|
110
|
+
color: #2563eb;
|
|
111
|
+
margin: 0 0 24px 0;
|
|
112
|
+
text-transform: uppercase;
|
|
113
|
+
letter-spacing: 0.5px;
|
|
114
|
+
display: flex;
|
|
115
|
+
align-items: center;
|
|
116
|
+
gap: 12px;
|
|
117
|
+
|
|
118
|
+
&::before {
|
|
119
|
+
content: '';
|
|
120
|
+
width: 4px;
|
|
121
|
+
height: 28px;
|
|
122
|
+
background: #2563eb;
|
|
123
|
+
border-radius: 2px;
|
|
124
|
+
}
|
|
125
|
+
`;
|
|
126
|
+
|
|
127
|
+
const EducationItem = styled.div`
|
|
128
|
+
margin-bottom: 20px;
|
|
129
|
+
background: white;
|
|
130
|
+
padding: 24px;
|
|
131
|
+
border-radius: 12px;
|
|
132
|
+
box-shadow: 0 2px 8px rgba(37, 99, 235, 0.08);
|
|
133
|
+
border-left: 4px solid #2563eb;
|
|
134
|
+
transition: all 0.2s ease;
|
|
135
|
+
|
|
136
|
+
&:hover {
|
|
137
|
+
box-shadow: 0 4px 12px rgba(37, 99, 235, 0.15);
|
|
138
|
+
transform: translateX(4px);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
&:last-child {
|
|
142
|
+
margin-bottom: 0;
|
|
143
|
+
}
|
|
144
|
+
`;
|
|
145
|
+
|
|
146
|
+
const Institution = styled.h3`
|
|
147
|
+
font-size: 19px;
|
|
148
|
+
font-weight: 700;
|
|
149
|
+
color: #1e40af;
|
|
150
|
+
margin: 0 0 8px 0;
|
|
151
|
+
`;
|
|
152
|
+
|
|
153
|
+
const Degree = styled.div`
|
|
154
|
+
font-size: 16px;
|
|
155
|
+
color: #374151;
|
|
156
|
+
margin-bottom: 6px;
|
|
157
|
+
font-weight: 600;
|
|
158
|
+
`;
|
|
159
|
+
|
|
160
|
+
const EducationMeta = styled.div`
|
|
161
|
+
font-size: 14px;
|
|
162
|
+
color: #6b7280;
|
|
163
|
+
display: flex;
|
|
164
|
+
gap: 12px;
|
|
165
|
+
flex-wrap: wrap;
|
|
166
|
+
`;
|
|
167
|
+
|
|
168
|
+
const Courses = styled.div`
|
|
169
|
+
margin-top: 8px;
|
|
170
|
+
font-size: 14px;
|
|
171
|
+
color: #4b5563;
|
|
172
|
+
|
|
173
|
+
strong {
|
|
174
|
+
color: #374151;
|
|
175
|
+
}
|
|
176
|
+
`;
|
|
177
|
+
|
|
178
|
+
const WorkItem = styled.div`
|
|
179
|
+
margin-bottom: 28px;
|
|
180
|
+
|
|
181
|
+
&:last-child {
|
|
182
|
+
margin-bottom: 0;
|
|
183
|
+
}
|
|
184
|
+
`;
|
|
185
|
+
|
|
186
|
+
const WorkHeader = styled.div`
|
|
187
|
+
display: flex;
|
|
188
|
+
justify-content: space-between;
|
|
189
|
+
align-items: baseline;
|
|
190
|
+
margin-bottom: 8px;
|
|
191
|
+
gap: 16px;
|
|
192
|
+
|
|
193
|
+
@media (max-width: 640px) {
|
|
194
|
+
flex-direction: column;
|
|
195
|
+
align-items: flex-start;
|
|
196
|
+
gap: 4px;
|
|
197
|
+
}
|
|
198
|
+
`;
|
|
199
|
+
|
|
200
|
+
const Position = styled.h3`
|
|
201
|
+
font-size: 16px;
|
|
202
|
+
font-weight: 600;
|
|
203
|
+
color: #111827;
|
|
204
|
+
margin: 0;
|
|
205
|
+
`;
|
|
206
|
+
|
|
207
|
+
const Company = styled.div`
|
|
208
|
+
font-size: 15px;
|
|
209
|
+
color: #2563eb;
|
|
210
|
+
font-weight: 500;
|
|
211
|
+
margin-top: 4px;
|
|
212
|
+
`;
|
|
213
|
+
|
|
214
|
+
const DateText = styled.div`
|
|
215
|
+
font-size: 13px;
|
|
216
|
+
color: #6b7280;
|
|
217
|
+
font-weight: 500;
|
|
218
|
+
white-space: nowrap;
|
|
219
|
+
`;
|
|
220
|
+
|
|
221
|
+
const WorkSummary = styled.p`
|
|
222
|
+
margin: 10px 0;
|
|
223
|
+
color: #4b5563;
|
|
224
|
+
line-height: 1.7;
|
|
225
|
+
font-size: 14px;
|
|
226
|
+
`;
|
|
227
|
+
|
|
228
|
+
const Highlights = styled.ul`
|
|
229
|
+
margin: 10px 0 0 0;
|
|
230
|
+
padding-left: 20px;
|
|
231
|
+
list-style-type: disc;
|
|
232
|
+
|
|
233
|
+
li {
|
|
234
|
+
margin: 6px 0;
|
|
235
|
+
color: #4b5563;
|
|
236
|
+
line-height: 1.7;
|
|
237
|
+
padding-left: 4px;
|
|
238
|
+
}
|
|
239
|
+
`;
|
|
240
|
+
|
|
241
|
+
const SkillsGrid = styled.div`
|
|
242
|
+
display: grid;
|
|
243
|
+
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
|
244
|
+
gap: 16px;
|
|
245
|
+
`;
|
|
246
|
+
|
|
247
|
+
const SkillCategory = styled.div`
|
|
248
|
+
padding: 14px;
|
|
249
|
+
background: #f0f9ff;
|
|
250
|
+
border-radius: 6px;
|
|
251
|
+
border-left: 3px solid #2563eb;
|
|
252
|
+
`;
|
|
253
|
+
|
|
254
|
+
const SkillName = styled.h4`
|
|
255
|
+
font-size: 14px;
|
|
256
|
+
font-weight: 600;
|
|
257
|
+
color: #111827;
|
|
258
|
+
margin: 0 0 6px 0;
|
|
259
|
+
`;
|
|
260
|
+
|
|
261
|
+
const SkillTags = styled.div`
|
|
262
|
+
font-size: 13px;
|
|
263
|
+
color: #6b7280;
|
|
264
|
+
line-height: 1.6;
|
|
265
|
+
`;
|
|
266
|
+
|
|
267
|
+
const ProjectItem = styled(WorkItem)``;
|
|
268
|
+
|
|
269
|
+
const ProjectTitle = styled(Position)`
|
|
270
|
+
font-size: 16px;
|
|
271
|
+
`;
|
|
272
|
+
|
|
273
|
+
const ProjectMeta = styled.div`
|
|
274
|
+
display: flex;
|
|
275
|
+
gap: 12px;
|
|
276
|
+
margin-top: 4px;
|
|
277
|
+
font-size: 13px;
|
|
278
|
+
color: #6b7280;
|
|
279
|
+
flex-wrap: wrap;
|
|
280
|
+
`;
|
|
281
|
+
|
|
282
|
+
const ProjectLink = styled.a`
|
|
283
|
+
color: #2563eb;
|
|
284
|
+
text-decoration: none;
|
|
285
|
+
|
|
286
|
+
&:hover {
|
|
287
|
+
text-decoration: underline;
|
|
288
|
+
}
|
|
289
|
+
`;
|
|
290
|
+
|
|
291
|
+
const AwardItem = styled(EducationItem)``;
|
|
292
|
+
|
|
293
|
+
const AwardTitle = styled(Institution)`
|
|
294
|
+
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI',
|
|
295
|
+
sans-serif;
|
|
296
|
+
font-size: 16px;
|
|
297
|
+
`;
|
|
298
|
+
|
|
299
|
+
const SimpleList = styled.div`
|
|
300
|
+
display: grid;
|
|
301
|
+
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
|
302
|
+
gap: 12px;
|
|
303
|
+
`;
|
|
304
|
+
|
|
305
|
+
const SimpleItem = styled.div`
|
|
306
|
+
font-size: 14px;
|
|
307
|
+
color: #4b5563;
|
|
308
|
+
|
|
309
|
+
strong {
|
|
310
|
+
color: #111827;
|
|
311
|
+
font-weight: 600;
|
|
312
|
+
}
|
|
313
|
+
`;
|
|
314
|
+
|
|
315
|
+
function Resume({ resume }) {
|
|
316
|
+
const {
|
|
317
|
+
basics = {},
|
|
318
|
+
work = [],
|
|
319
|
+
education = [],
|
|
320
|
+
skills = [],
|
|
321
|
+
projects = [],
|
|
322
|
+
volunteer = [],
|
|
323
|
+
awards = [],
|
|
324
|
+
publications = [],
|
|
325
|
+
languages = [],
|
|
326
|
+
interests = [],
|
|
327
|
+
references = [],
|
|
328
|
+
} = resume;
|
|
329
|
+
|
|
330
|
+
return (
|
|
331
|
+
<Layout>
|
|
332
|
+
<Header>
|
|
333
|
+
<Name>{basics.name}</Name>
|
|
334
|
+
{basics.label && <Label>{basics.label}</Label>}
|
|
335
|
+
<StyledContactInfo basics={basics} />
|
|
336
|
+
</Header>
|
|
337
|
+
|
|
338
|
+
<Content>
|
|
339
|
+
{basics.summary && <Summary>{basics.summary}</Summary>}
|
|
340
|
+
|
|
341
|
+
{education?.length > 0 && (
|
|
342
|
+
<Section>
|
|
343
|
+
<StyledSectionTitle>Education</StyledSectionTitle>
|
|
344
|
+
{education.map((edu, index) => (
|
|
345
|
+
<EducationItem key={index}>
|
|
346
|
+
<Institution>{edu.institution}</Institution>
|
|
347
|
+
<Degree>
|
|
348
|
+
{edu.studyType} in {edu.area}
|
|
349
|
+
</Degree>
|
|
350
|
+
<EducationMeta>
|
|
351
|
+
<DateText>
|
|
352
|
+
<DateRange
|
|
353
|
+
startDate={edu.startDate}
|
|
354
|
+
endDate={edu.endDate}
|
|
355
|
+
/>
|
|
356
|
+
</DateText>
|
|
357
|
+
{edu.score && <span>GPA: {edu.score}</span>}
|
|
358
|
+
</EducationMeta>
|
|
359
|
+
{edu.courses?.length > 0 && (
|
|
360
|
+
<Courses>
|
|
361
|
+
<strong>Relevant Coursework:</strong>{' '}
|
|
362
|
+
{edu.courses.join(', ')}
|
|
363
|
+
</Courses>
|
|
364
|
+
)}
|
|
365
|
+
</EducationItem>
|
|
366
|
+
))}
|
|
367
|
+
</Section>
|
|
368
|
+
)}
|
|
369
|
+
|
|
370
|
+
{skills?.length > 0 && (
|
|
371
|
+
<Section>
|
|
372
|
+
<StyledSectionTitle>Skills</StyledSectionTitle>
|
|
373
|
+
<SkillsGrid>
|
|
374
|
+
{skills.map((skill, index) => (
|
|
375
|
+
<SkillCategory key={index}>
|
|
376
|
+
<SkillName>{skill.name}</SkillName>
|
|
377
|
+
{skill.keywords?.length > 0 && (
|
|
378
|
+
<SkillTags>{skill.keywords.join(', ')}</SkillTags>
|
|
379
|
+
)}
|
|
380
|
+
</SkillCategory>
|
|
381
|
+
))}
|
|
382
|
+
</SkillsGrid>
|
|
383
|
+
</Section>
|
|
384
|
+
)}
|
|
385
|
+
|
|
386
|
+
{projects?.length > 0 && (
|
|
387
|
+
<Section>
|
|
388
|
+
<StyledSectionTitle>Projects</StyledSectionTitle>
|
|
389
|
+
{projects.map((project, index) => (
|
|
390
|
+
<ProjectItem key={index}>
|
|
391
|
+
<ProjectTitle>{project.name}</ProjectTitle>
|
|
392
|
+
<ProjectMeta>
|
|
393
|
+
{project.startDate && (
|
|
394
|
+
<DateText>
|
|
395
|
+
<DateRange
|
|
396
|
+
startDate={project.startDate}
|
|
397
|
+
endDate={project.endDate}
|
|
398
|
+
/>
|
|
399
|
+
</DateText>
|
|
400
|
+
)}
|
|
401
|
+
{project.url && (
|
|
402
|
+
<ProjectLink
|
|
403
|
+
href={project.url}
|
|
404
|
+
target="_blank"
|
|
405
|
+
rel="noopener noreferrer"
|
|
406
|
+
>
|
|
407
|
+
View Project
|
|
408
|
+
</ProjectLink>
|
|
409
|
+
)}
|
|
410
|
+
</ProjectMeta>
|
|
411
|
+
{project.description && (
|
|
412
|
+
<WorkSummary>{project.description}</WorkSummary>
|
|
413
|
+
)}
|
|
414
|
+
{project.highlights?.length > 0 && (
|
|
415
|
+
<Highlights>
|
|
416
|
+
{project.highlights.map((highlight, i) => (
|
|
417
|
+
<li key={i}>{highlight}</li>
|
|
418
|
+
))}
|
|
419
|
+
</Highlights>
|
|
420
|
+
)}
|
|
421
|
+
</ProjectItem>
|
|
422
|
+
))}
|
|
423
|
+
</Section>
|
|
424
|
+
)}
|
|
425
|
+
|
|
426
|
+
{work?.length > 0 && (
|
|
427
|
+
<Section>
|
|
428
|
+
<StyledSectionTitle>Experience</StyledSectionTitle>
|
|
429
|
+
{work.map((job, index) => (
|
|
430
|
+
<WorkItem key={index}>
|
|
431
|
+
<WorkHeader>
|
|
432
|
+
<div>
|
|
433
|
+
<Position>{job.position}</Position>
|
|
434
|
+
{job.name && <Company>{job.name}</Company>}
|
|
435
|
+
</div>
|
|
436
|
+
<DateText>
|
|
437
|
+
<DateRange
|
|
438
|
+
startDate={job.startDate}
|
|
439
|
+
endDate={job.endDate}
|
|
440
|
+
/>
|
|
441
|
+
</DateText>
|
|
442
|
+
</WorkHeader>
|
|
443
|
+
{job.summary && <WorkSummary>{job.summary}</WorkSummary>}
|
|
444
|
+
{job.highlights?.length > 0 && (
|
|
445
|
+
<Highlights>
|
|
446
|
+
{job.highlights.map((highlight, i) => (
|
|
447
|
+
<li key={i}>{highlight}</li>
|
|
448
|
+
))}
|
|
449
|
+
</Highlights>
|
|
450
|
+
)}
|
|
451
|
+
</WorkItem>
|
|
452
|
+
))}
|
|
453
|
+
</Section>
|
|
454
|
+
)}
|
|
455
|
+
|
|
456
|
+
{volunteer?.length > 0 && (
|
|
457
|
+
<Section>
|
|
458
|
+
<StyledSectionTitle>Volunteer Experience</StyledSectionTitle>
|
|
459
|
+
{volunteer.map((vol, index) => (
|
|
460
|
+
<WorkItem key={index}>
|
|
461
|
+
<WorkHeader>
|
|
462
|
+
<div>
|
|
463
|
+
<Position>{vol.position}</Position>
|
|
464
|
+
{vol.organization && <Company>{vol.organization}</Company>}
|
|
465
|
+
</div>
|
|
466
|
+
{(vol.startDate || vol.endDate) && (
|
|
467
|
+
<DateText>
|
|
468
|
+
<DateRange
|
|
469
|
+
startDate={vol.startDate}
|
|
470
|
+
endDate={vol.endDate}
|
|
471
|
+
/>
|
|
472
|
+
</DateText>
|
|
473
|
+
)}
|
|
474
|
+
</WorkHeader>
|
|
475
|
+
{vol.summary && <WorkSummary>{vol.summary}</WorkSummary>}
|
|
476
|
+
{vol.highlights?.length > 0 && (
|
|
477
|
+
<Highlights>
|
|
478
|
+
{vol.highlights.map((highlight, i) => (
|
|
479
|
+
<li key={i}>{highlight}</li>
|
|
480
|
+
))}
|
|
481
|
+
</Highlights>
|
|
482
|
+
)}
|
|
483
|
+
</WorkItem>
|
|
484
|
+
))}
|
|
485
|
+
</Section>
|
|
486
|
+
)}
|
|
487
|
+
|
|
488
|
+
{awards?.length > 0 && (
|
|
489
|
+
<Section>
|
|
490
|
+
<StyledSectionTitle>Awards & Honors</StyledSectionTitle>
|
|
491
|
+
{awards.map((award, index) => (
|
|
492
|
+
<AwardItem key={index}>
|
|
493
|
+
<AwardTitle>{award.title}</AwardTitle>
|
|
494
|
+
{award.awarder && <Degree>Awarded by {award.awarder}</Degree>}
|
|
495
|
+
{award.date && <DateText>{award.date}</DateText>}
|
|
496
|
+
{award.summary && <WorkSummary>{award.summary}</WorkSummary>}
|
|
497
|
+
</AwardItem>
|
|
498
|
+
))}
|
|
499
|
+
</Section>
|
|
500
|
+
)}
|
|
501
|
+
|
|
502
|
+
{publications?.length > 0 && (
|
|
503
|
+
<Section>
|
|
504
|
+
<StyledSectionTitle>Publications</StyledSectionTitle>
|
|
505
|
+
{publications.map((pub, index) => (
|
|
506
|
+
<AwardItem key={index}>
|
|
507
|
+
<AwardTitle>{pub.name}</AwardTitle>
|
|
508
|
+
{pub.publisher && <Degree>Published by {pub.publisher}</Degree>}
|
|
509
|
+
{pub.releaseDate && <DateText>{pub.releaseDate}</DateText>}
|
|
510
|
+
{pub.summary && <WorkSummary>{pub.summary}</WorkSummary>}
|
|
511
|
+
</AwardItem>
|
|
512
|
+
))}
|
|
513
|
+
</Section>
|
|
514
|
+
)}
|
|
515
|
+
|
|
516
|
+
{languages?.length > 0 && (
|
|
517
|
+
<Section>
|
|
518
|
+
<StyledSectionTitle>Languages</StyledSectionTitle>
|
|
519
|
+
<SimpleList>
|
|
520
|
+
{languages.map((lang, index) => (
|
|
521
|
+
<SimpleItem key={index}>
|
|
522
|
+
<strong>{lang.language}</strong>
|
|
523
|
+
{lang.fluency && ` - ${lang.fluency}`}
|
|
524
|
+
</SimpleItem>
|
|
525
|
+
))}
|
|
526
|
+
</SimpleList>
|
|
527
|
+
</Section>
|
|
528
|
+
)}
|
|
529
|
+
|
|
530
|
+
{interests?.length > 0 && (
|
|
531
|
+
<Section>
|
|
532
|
+
<StyledSectionTitle>Interests</StyledSectionTitle>
|
|
533
|
+
<SkillsGrid>
|
|
534
|
+
{interests.map((interest, index) => (
|
|
535
|
+
<SkillCategory key={index}>
|
|
536
|
+
<SkillName>{interest.name}</SkillName>
|
|
537
|
+
{interest.keywords?.length > 0 && (
|
|
538
|
+
<SkillTags>{interest.keywords.join(', ')}</SkillTags>
|
|
539
|
+
)}
|
|
540
|
+
</SkillCategory>
|
|
541
|
+
))}
|
|
542
|
+
</SkillsGrid>
|
|
543
|
+
</Section>
|
|
544
|
+
)}
|
|
545
|
+
|
|
546
|
+
{references?.length > 0 && (
|
|
547
|
+
<Section>
|
|
548
|
+
<StyledSectionTitle>References</StyledSectionTitle>
|
|
549
|
+
{references.map((ref, index) => (
|
|
550
|
+
<AwardItem key={index}>
|
|
551
|
+
<AwardTitle>{ref.name}</AwardTitle>
|
|
552
|
+
{ref.reference && <WorkSummary>{ref.reference}</WorkSummary>}
|
|
553
|
+
</AwardItem>
|
|
554
|
+
))}
|
|
555
|
+
</Section>
|
|
556
|
+
)}
|
|
557
|
+
</Content>
|
|
558
|
+
</Layout>
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
export default Resume;
|
package/src/index.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { renderToString } from 'react-dom/server';
|
|
3
|
+
import { ServerStyleSheet } from 'styled-components';
|
|
4
|
+
import Resume from './Resume.jsx';
|
|
5
|
+
|
|
6
|
+
export function render(resume) {
|
|
7
|
+
const sheet = new ServerStyleSheet();
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
const html = renderToString(
|
|
11
|
+
sheet.collectStyles(<Resume resume={resume} />)
|
|
12
|
+
);
|
|
13
|
+
const styles = sheet.getStyleTags();
|
|
14
|
+
|
|
15
|
+
return `<!DOCTYPE html>
|
|
16
|
+
<html lang="en">
|
|
17
|
+
<head>
|
|
18
|
+
<meta charset="UTF-8">
|
|
19
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
20
|
+
<title>${resume.basics?.name || 'Resume'} - Resume</title>
|
|
21
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
22
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
23
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
24
|
+
${styles}
|
|
25
|
+
<style>
|
|
26
|
+
* {
|
|
27
|
+
box-sizing: border-box;
|
|
28
|
+
margin: 0;
|
|
29
|
+
padding: 0;
|
|
30
|
+
}
|
|
31
|
+
body {
|
|
32
|
+
margin: 0;
|
|
33
|
+
padding: 0;
|
|
34
|
+
background: #f9fafb;
|
|
35
|
+
}
|
|
36
|
+
@media print {
|
|
37
|
+
body {
|
|
38
|
+
background: white;
|
|
39
|
+
}
|
|
40
|
+
@page {
|
|
41
|
+
margin: 0.5in;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
</style>
|
|
45
|
+
</head>
|
|
46
|
+
<body>
|
|
47
|
+
${html}
|
|
48
|
+
</body>
|
|
49
|
+
</html>`;
|
|
50
|
+
} finally {
|
|
51
|
+
sheet.seal();
|
|
52
|
+
}
|
|
53
|
+
}
|