jsonresume-theme-operations-precision 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 +74 -0
- package/package.json +24 -0
- package/src/Resume.jsx +478 -0
- package/src/index.js +53 -0
package/README.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Operations Precision - JSON Resume Theme
|
|
2
|
+
|
|
3
|
+
A clean, efficient JSON Resume theme designed for operations professionals, systems thinkers, and process-oriented individuals.
|
|
4
|
+
|
|
5
|
+
## Design Philosophy
|
|
6
|
+
|
|
7
|
+
**Vibe:** Efficient, dependable, systems-oriented
|
|
8
|
+
**Layout:** Single column with clean structure and efficient use of space
|
|
9
|
+
**Typography:** Sans-serif (Inter), compact but readable (15px body text)
|
|
10
|
+
**Color:** Teal accent (#0f766e) representing operational efficiency
|
|
11
|
+
|
|
12
|
+
## Key Features
|
|
13
|
+
|
|
14
|
+
- **Compact Design**: Efficient spacing optimized for readability without wasted space
|
|
15
|
+
- **Tabular Skills Layout**: Skills displayed in a clean grid format with clear labels
|
|
16
|
+
- **Process-Focused**: Emphasizes systematic approach and measurable improvements
|
|
17
|
+
- **Professional Typography**: Upper-case section titles with consistent letter spacing
|
|
18
|
+
- **Numeric Precision**: Tabular numbers for dates ensure perfect alignment
|
|
19
|
+
- **Print-Optimized**: Clean structure that prints beautifully
|
|
20
|
+
|
|
21
|
+
## Perfect For
|
|
22
|
+
|
|
23
|
+
- Operations Managers
|
|
24
|
+
- Logistics Professionals
|
|
25
|
+
- Process Engineers
|
|
26
|
+
- Systems Analysts
|
|
27
|
+
- Project Managers
|
|
28
|
+
- Quality Assurance Specialists
|
|
29
|
+
|
|
30
|
+
## Supported Sections
|
|
31
|
+
|
|
32
|
+
All 12 standard JSON Resume sections are fully supported:
|
|
33
|
+
|
|
34
|
+
- Basics (name, contact, summary)
|
|
35
|
+
- Work Experience
|
|
36
|
+
- Skills & Tools (tabular layout)
|
|
37
|
+
- Education
|
|
38
|
+
- Projects
|
|
39
|
+
- Volunteer
|
|
40
|
+
- Awards
|
|
41
|
+
- Publications
|
|
42
|
+
- Languages (two-column grid)
|
|
43
|
+
- Interests (two-column grid)
|
|
44
|
+
- References
|
|
45
|
+
|
|
46
|
+
## Design Details
|
|
47
|
+
|
|
48
|
+
### Color Palette
|
|
49
|
+
|
|
50
|
+
- **Primary Accent**: `#0f766e` (teal)
|
|
51
|
+
- **Headings**: `#111827` (near-black)
|
|
52
|
+
- **Body Text**: `#4b5563` (dark gray)
|
|
53
|
+
- **Metadata**: `#6b7280` (medium gray)
|
|
54
|
+
- **Background**: White
|
|
55
|
+
|
|
56
|
+
### Typography
|
|
57
|
+
|
|
58
|
+
- **Font Family**: Inter (with system font fallbacks)
|
|
59
|
+
- **Body Size**: 15px (10.5pt equivalent)
|
|
60
|
+
- **Line Height**: 1.5-1.6 (compact but readable)
|
|
61
|
+
- **Section Titles**: Uppercase with increased letter spacing
|
|
62
|
+
- **Emphasis**: Tabular numbers for date alignment
|
|
63
|
+
|
|
64
|
+
### Layout Features
|
|
65
|
+
|
|
66
|
+
- **Max Width**: 850px for optimal readability
|
|
67
|
+
- **Skills Table**: 140px label column + flexible content
|
|
68
|
+
- **Two-Column Grids**: Used for languages and interests
|
|
69
|
+
- **Page Break Control**: Sections avoid breaking across pages
|
|
70
|
+
- **Responsive**: Adapts gracefully to mobile screens
|
|
71
|
+
|
|
72
|
+
## License
|
|
73
|
+
|
|
74
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "jsonresume-theme-operations-precision",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Operations professionals - show your systematic approach, process improvements",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"jsonresume",
|
|
10
|
+
"theme",
|
|
11
|
+
"operations",
|
|
12
|
+
"precision",
|
|
13
|
+
"efficiency",
|
|
14
|
+
"systems"
|
|
15
|
+
],
|
|
16
|
+
"peerDependencies": {
|
|
17
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
18
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"styled-components": "^6.1.19",
|
|
22
|
+
"@resume/core": "0.1.0"
|
|
23
|
+
}
|
|
24
|
+
}
|
package/src/Resume.jsx
ADDED
|
@@ -0,0 +1,478 @@
|
|
|
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: 850px;
|
|
7
|
+
margin: 0 auto;
|
|
8
|
+
padding: 40px 35px;
|
|
9
|
+
background: white;
|
|
10
|
+
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI',
|
|
11
|
+
sans-serif;
|
|
12
|
+
color: #1f2937;
|
|
13
|
+
font-size: 15px;
|
|
14
|
+
line-height: 1.5;
|
|
15
|
+
|
|
16
|
+
@media print {
|
|
17
|
+
padding: 30px;
|
|
18
|
+
}
|
|
19
|
+
`;
|
|
20
|
+
|
|
21
|
+
const Header = styled.header`
|
|
22
|
+
margin-bottom: 35px;
|
|
23
|
+
padding-bottom: 20px;
|
|
24
|
+
border-bottom: 2px solid #0f766e;
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
const Name = styled.h1`
|
|
28
|
+
font-size: 32px;
|
|
29
|
+
font-weight: 700;
|
|
30
|
+
color: #0f766e;
|
|
31
|
+
margin: 0 0 6px 0;
|
|
32
|
+
letter-spacing: -0.3px;
|
|
33
|
+
`;
|
|
34
|
+
|
|
35
|
+
const Label = styled.div`
|
|
36
|
+
font-size: 15px;
|
|
37
|
+
color: #4b5563;
|
|
38
|
+
margin-bottom: 16px;
|
|
39
|
+
font-weight: 500;
|
|
40
|
+
text-transform: uppercase;
|
|
41
|
+
letter-spacing: 0.5px;
|
|
42
|
+
`;
|
|
43
|
+
|
|
44
|
+
const StyledContactInfo = styled(ContactInfo)`
|
|
45
|
+
font-size: 14px;
|
|
46
|
+
display: flex;
|
|
47
|
+
flex-wrap: wrap;
|
|
48
|
+
gap: 12px;
|
|
49
|
+
|
|
50
|
+
a {
|
|
51
|
+
font-size: 14px;
|
|
52
|
+
color: #0f766e;
|
|
53
|
+
text-decoration: none;
|
|
54
|
+
|
|
55
|
+
&:hover {
|
|
56
|
+
text-decoration: underline;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
`;
|
|
60
|
+
|
|
61
|
+
const Summary = styled.p`
|
|
62
|
+
font-size: 15px;
|
|
63
|
+
line-height: 1.6;
|
|
64
|
+
color: #374151;
|
|
65
|
+
margin: 16px 0 0 0;
|
|
66
|
+
text-align: justify;
|
|
67
|
+
`;
|
|
68
|
+
|
|
69
|
+
const StyledSectionTitle = styled(SectionTitle)`
|
|
70
|
+
font-size: 16px;
|
|
71
|
+
font-weight: 700;
|
|
72
|
+
color: #0f766e;
|
|
73
|
+
margin: 28px 0 16px 0;
|
|
74
|
+
padding-bottom: 6px;
|
|
75
|
+
border-bottom: 2px solid #0f766e;
|
|
76
|
+
text-transform: uppercase;
|
|
77
|
+
letter-spacing: 0.8px;
|
|
78
|
+
`;
|
|
79
|
+
|
|
80
|
+
const WorkItem = styled.div`
|
|
81
|
+
margin-bottom: 24px;
|
|
82
|
+
page-break-inside: avoid;
|
|
83
|
+
|
|
84
|
+
&:last-child {
|
|
85
|
+
margin-bottom: 0;
|
|
86
|
+
}
|
|
87
|
+
`;
|
|
88
|
+
|
|
89
|
+
const WorkHeader = styled.div`
|
|
90
|
+
display: grid;
|
|
91
|
+
grid-template-columns: 1fr auto;
|
|
92
|
+
gap: 12px;
|
|
93
|
+
margin-bottom: 6px;
|
|
94
|
+
align-items: baseline;
|
|
95
|
+
|
|
96
|
+
@media (max-width: 640px) {
|
|
97
|
+
grid-template-columns: 1fr;
|
|
98
|
+
gap: 4px;
|
|
99
|
+
}
|
|
100
|
+
`;
|
|
101
|
+
|
|
102
|
+
const Position = styled.h3`
|
|
103
|
+
font-size: 16px;
|
|
104
|
+
font-weight: 600;
|
|
105
|
+
color: #111827;
|
|
106
|
+
margin: 0;
|
|
107
|
+
`;
|
|
108
|
+
|
|
109
|
+
const Company = styled.div`
|
|
110
|
+
font-size: 14px;
|
|
111
|
+
color: #0f766e;
|
|
112
|
+
font-weight: 500;
|
|
113
|
+
margin-top: 2px;
|
|
114
|
+
`;
|
|
115
|
+
|
|
116
|
+
const DateText = styled.div`
|
|
117
|
+
font-size: 13px;
|
|
118
|
+
color: #6b7280;
|
|
119
|
+
font-weight: 500;
|
|
120
|
+
white-space: nowrap;
|
|
121
|
+
font-variant-numeric: tabular-nums;
|
|
122
|
+
`;
|
|
123
|
+
|
|
124
|
+
const WorkSummary = styled.p`
|
|
125
|
+
margin: 8px 0;
|
|
126
|
+
color: #4b5563;
|
|
127
|
+
line-height: 1.6;
|
|
128
|
+
font-size: 14px;
|
|
129
|
+
`;
|
|
130
|
+
|
|
131
|
+
const Highlights = styled.ul`
|
|
132
|
+
margin: 8px 0 0 0;
|
|
133
|
+
padding-left: 18px;
|
|
134
|
+
list-style-type: square;
|
|
135
|
+
|
|
136
|
+
li {
|
|
137
|
+
margin: 6px 0;
|
|
138
|
+
color: #4b5563;
|
|
139
|
+
line-height: 1.6;
|
|
140
|
+
font-size: 14px;
|
|
141
|
+
padding-left: 4px;
|
|
142
|
+
}
|
|
143
|
+
`;
|
|
144
|
+
|
|
145
|
+
const EducationItem = styled.div`
|
|
146
|
+
margin-bottom: 20px;
|
|
147
|
+
page-break-inside: avoid;
|
|
148
|
+
|
|
149
|
+
&:last-child {
|
|
150
|
+
margin-bottom: 0;
|
|
151
|
+
}
|
|
152
|
+
`;
|
|
153
|
+
|
|
154
|
+
const Institution = styled.h3`
|
|
155
|
+
font-size: 15px;
|
|
156
|
+
font-weight: 600;
|
|
157
|
+
color: #111827;
|
|
158
|
+
margin: 0 0 4px 0;
|
|
159
|
+
`;
|
|
160
|
+
|
|
161
|
+
const Degree = styled.div`
|
|
162
|
+
font-size: 14px;
|
|
163
|
+
color: #4b5563;
|
|
164
|
+
margin-bottom: 3px;
|
|
165
|
+
`;
|
|
166
|
+
|
|
167
|
+
const EducationDate = styled.div`
|
|
168
|
+
font-size: 13px;
|
|
169
|
+
color: #6b7280;
|
|
170
|
+
font-variant-numeric: tabular-nums;
|
|
171
|
+
`;
|
|
172
|
+
|
|
173
|
+
const SkillsTable = styled.div`
|
|
174
|
+
display: grid;
|
|
175
|
+
grid-template-columns: 140px 1fr;
|
|
176
|
+
gap: 12px 20px;
|
|
177
|
+
border: 1px solid #e5e7eb;
|
|
178
|
+
padding: 16px;
|
|
179
|
+
background: #f9fafb;
|
|
180
|
+
|
|
181
|
+
@media (max-width: 640px) {
|
|
182
|
+
grid-template-columns: 1fr;
|
|
183
|
+
gap: 12px;
|
|
184
|
+
}
|
|
185
|
+
`;
|
|
186
|
+
|
|
187
|
+
const SkillName = styled.div`
|
|
188
|
+
font-size: 14px;
|
|
189
|
+
font-weight: 600;
|
|
190
|
+
color: #0f766e;
|
|
191
|
+
text-transform: uppercase;
|
|
192
|
+
letter-spacing: 0.5px;
|
|
193
|
+
align-self: start;
|
|
194
|
+
padding-top: 2px;
|
|
195
|
+
`;
|
|
196
|
+
|
|
197
|
+
const SkillTags = styled.div`
|
|
198
|
+
font-size: 14px;
|
|
199
|
+
color: #4b5563;
|
|
200
|
+
line-height: 1.5;
|
|
201
|
+
`;
|
|
202
|
+
|
|
203
|
+
const ProjectItem = styled.div`
|
|
204
|
+
margin-bottom: 20px;
|
|
205
|
+
page-break-inside: avoid;
|
|
206
|
+
|
|
207
|
+
&:last-child {
|
|
208
|
+
margin-bottom: 0;
|
|
209
|
+
}
|
|
210
|
+
`;
|
|
211
|
+
|
|
212
|
+
const ProjectHeader = styled.div`
|
|
213
|
+
display: flex;
|
|
214
|
+
justify-content: space-between;
|
|
215
|
+
align-items: baseline;
|
|
216
|
+
gap: 12px;
|
|
217
|
+
margin-bottom: 6px;
|
|
218
|
+
|
|
219
|
+
@media (max-width: 640px) {
|
|
220
|
+
flex-direction: column;
|
|
221
|
+
gap: 4px;
|
|
222
|
+
}
|
|
223
|
+
`;
|
|
224
|
+
|
|
225
|
+
const ProjectName = styled.h3`
|
|
226
|
+
font-size: 15px;
|
|
227
|
+
font-weight: 600;
|
|
228
|
+
color: #111827;
|
|
229
|
+
margin: 0;
|
|
230
|
+
`;
|
|
231
|
+
|
|
232
|
+
const TwoColumnGrid = styled.div`
|
|
233
|
+
display: grid;
|
|
234
|
+
grid-template-columns: repeat(2, 1fr);
|
|
235
|
+
gap: 12px 20px;
|
|
236
|
+
|
|
237
|
+
@media (max-width: 640px) {
|
|
238
|
+
grid-template-columns: 1fr;
|
|
239
|
+
}
|
|
240
|
+
`;
|
|
241
|
+
|
|
242
|
+
const CompactItem = styled.div`
|
|
243
|
+
font-size: 14px;
|
|
244
|
+
line-height: 1.5;
|
|
245
|
+
color: #4b5563;
|
|
246
|
+
`;
|
|
247
|
+
|
|
248
|
+
const CompactTitle = styled.span`
|
|
249
|
+
font-weight: 600;
|
|
250
|
+
color: #111827;
|
|
251
|
+
`;
|
|
252
|
+
|
|
253
|
+
function Resume({ resume }) {
|
|
254
|
+
const {
|
|
255
|
+
basics = {},
|
|
256
|
+
work = [],
|
|
257
|
+
education = [],
|
|
258
|
+
skills = [],
|
|
259
|
+
projects = [],
|
|
260
|
+
volunteer = [],
|
|
261
|
+
awards = [],
|
|
262
|
+
publications = [],
|
|
263
|
+
languages = [],
|
|
264
|
+
interests = [],
|
|
265
|
+
references = [],
|
|
266
|
+
} = resume;
|
|
267
|
+
|
|
268
|
+
return (
|
|
269
|
+
<Layout>
|
|
270
|
+
<Header>
|
|
271
|
+
<Name>{basics.name}</Name>
|
|
272
|
+
{basics.label && <Label>{basics.label}</Label>}
|
|
273
|
+
<StyledContactInfo basics={basics} />
|
|
274
|
+
{basics.summary && <Summary>{basics.summary}</Summary>}
|
|
275
|
+
</Header>
|
|
276
|
+
|
|
277
|
+
{work?.length > 0 && (
|
|
278
|
+
<Section>
|
|
279
|
+
<StyledSectionTitle>Experience</StyledSectionTitle>
|
|
280
|
+
{work.map((job, index) => (
|
|
281
|
+
<WorkItem key={index}>
|
|
282
|
+
<WorkHeader>
|
|
283
|
+
<div>
|
|
284
|
+
<Position>{job.position}</Position>
|
|
285
|
+
{job.name && <Company>{job.name}</Company>}
|
|
286
|
+
</div>
|
|
287
|
+
<DateText>
|
|
288
|
+
<DateRange startDate={job.startDate} endDate={job.endDate} />
|
|
289
|
+
</DateText>
|
|
290
|
+
</WorkHeader>
|
|
291
|
+
{job.summary && <WorkSummary>{job.summary}</WorkSummary>}
|
|
292
|
+
{job.highlights?.length > 0 && (
|
|
293
|
+
<Highlights>
|
|
294
|
+
{job.highlights.map((highlight, i) => (
|
|
295
|
+
<li key={i}>{highlight}</li>
|
|
296
|
+
))}
|
|
297
|
+
</Highlights>
|
|
298
|
+
)}
|
|
299
|
+
</WorkItem>
|
|
300
|
+
))}
|
|
301
|
+
</Section>
|
|
302
|
+
)}
|
|
303
|
+
|
|
304
|
+
{skills?.length > 0 && (
|
|
305
|
+
<Section>
|
|
306
|
+
<StyledSectionTitle>Skills & Tools</StyledSectionTitle>
|
|
307
|
+
<SkillsTable>
|
|
308
|
+
{skills.map((skill, index) => (
|
|
309
|
+
<React.Fragment key={index}>
|
|
310
|
+
<SkillName>{skill.name}</SkillName>
|
|
311
|
+
<SkillTags>
|
|
312
|
+
{skill.keywords?.length > 0
|
|
313
|
+
? skill.keywords.join(' • ')
|
|
314
|
+
: '—'}
|
|
315
|
+
</SkillTags>
|
|
316
|
+
</React.Fragment>
|
|
317
|
+
))}
|
|
318
|
+
</SkillsTable>
|
|
319
|
+
</Section>
|
|
320
|
+
)}
|
|
321
|
+
|
|
322
|
+
{education?.length > 0 && (
|
|
323
|
+
<Section>
|
|
324
|
+
<StyledSectionTitle>Education</StyledSectionTitle>
|
|
325
|
+
{education.map((edu, index) => (
|
|
326
|
+
<EducationItem key={index}>
|
|
327
|
+
<Institution>{edu.institution}</Institution>
|
|
328
|
+
<Degree>
|
|
329
|
+
{edu.studyType} in {edu.area}
|
|
330
|
+
{edu.score && ` • ${edu.score}`}
|
|
331
|
+
</Degree>
|
|
332
|
+
<EducationDate>
|
|
333
|
+
<DateRange startDate={edu.startDate} endDate={edu.endDate} />
|
|
334
|
+
</EducationDate>
|
|
335
|
+
</EducationItem>
|
|
336
|
+
))}
|
|
337
|
+
</Section>
|
|
338
|
+
)}
|
|
339
|
+
|
|
340
|
+
{projects?.length > 0 && (
|
|
341
|
+
<Section>
|
|
342
|
+
<StyledSectionTitle>Projects</StyledSectionTitle>
|
|
343
|
+
{projects.map((project, index) => (
|
|
344
|
+
<ProjectItem key={index}>
|
|
345
|
+
<ProjectHeader>
|
|
346
|
+
<ProjectName>{project.name}</ProjectName>
|
|
347
|
+
{(project.startDate || project.endDate) && (
|
|
348
|
+
<DateText>
|
|
349
|
+
<DateRange
|
|
350
|
+
startDate={project.startDate}
|
|
351
|
+
endDate={project.endDate}
|
|
352
|
+
/>
|
|
353
|
+
</DateText>
|
|
354
|
+
)}
|
|
355
|
+
</ProjectHeader>
|
|
356
|
+
{project.description && (
|
|
357
|
+
<WorkSummary>{project.description}</WorkSummary>
|
|
358
|
+
)}
|
|
359
|
+
{project.highlights?.length > 0 && (
|
|
360
|
+
<Highlights>
|
|
361
|
+
{project.highlights.map((highlight, i) => (
|
|
362
|
+
<li key={i}>{highlight}</li>
|
|
363
|
+
))}
|
|
364
|
+
</Highlights>
|
|
365
|
+
)}
|
|
366
|
+
</ProjectItem>
|
|
367
|
+
))}
|
|
368
|
+
</Section>
|
|
369
|
+
)}
|
|
370
|
+
|
|
371
|
+
{volunteer?.length > 0 && (
|
|
372
|
+
<Section>
|
|
373
|
+
<StyledSectionTitle>Volunteer</StyledSectionTitle>
|
|
374
|
+
{volunteer.map((vol, index) => (
|
|
375
|
+
<WorkItem key={index}>
|
|
376
|
+
<WorkHeader>
|
|
377
|
+
<div>
|
|
378
|
+
<Position>{vol.position}</Position>
|
|
379
|
+
{vol.organization && <Company>{vol.organization}</Company>}
|
|
380
|
+
</div>
|
|
381
|
+
{(vol.startDate || vol.endDate) && (
|
|
382
|
+
<DateText>
|
|
383
|
+
<DateRange
|
|
384
|
+
startDate={vol.startDate}
|
|
385
|
+
endDate={vol.endDate}
|
|
386
|
+
/>
|
|
387
|
+
</DateText>
|
|
388
|
+
)}
|
|
389
|
+
</WorkHeader>
|
|
390
|
+
{vol.summary && <WorkSummary>{vol.summary}</WorkSummary>}
|
|
391
|
+
{vol.highlights?.length > 0 && (
|
|
392
|
+
<Highlights>
|
|
393
|
+
{vol.highlights.map((highlight, i) => (
|
|
394
|
+
<li key={i}>{highlight}</li>
|
|
395
|
+
))}
|
|
396
|
+
</Highlights>
|
|
397
|
+
)}
|
|
398
|
+
</WorkItem>
|
|
399
|
+
))}
|
|
400
|
+
</Section>
|
|
401
|
+
)}
|
|
402
|
+
|
|
403
|
+
{awards?.length > 0 && (
|
|
404
|
+
<Section>
|
|
405
|
+
<StyledSectionTitle>Awards</StyledSectionTitle>
|
|
406
|
+
{awards.map((award, index) => (
|
|
407
|
+
<EducationItem key={index}>
|
|
408
|
+
<Institution>{award.title}</Institution>
|
|
409
|
+
{award.awarder && <Degree>Awarded by {award.awarder}</Degree>}
|
|
410
|
+
{award.date && <EducationDate>{award.date}</EducationDate>}
|
|
411
|
+
{award.summary && <WorkSummary>{award.summary}</WorkSummary>}
|
|
412
|
+
</EducationItem>
|
|
413
|
+
))}
|
|
414
|
+
</Section>
|
|
415
|
+
)}
|
|
416
|
+
|
|
417
|
+
{publications?.length > 0 && (
|
|
418
|
+
<Section>
|
|
419
|
+
<StyledSectionTitle>Publications</StyledSectionTitle>
|
|
420
|
+
{publications.map((pub, index) => (
|
|
421
|
+
<EducationItem key={index}>
|
|
422
|
+
<Institution>{pub.name}</Institution>
|
|
423
|
+
{pub.publisher && <Degree>Published by {pub.publisher}</Degree>}
|
|
424
|
+
{pub.releaseDate && (
|
|
425
|
+
<EducationDate>{pub.releaseDate}</EducationDate>
|
|
426
|
+
)}
|
|
427
|
+
{pub.summary && <WorkSummary>{pub.summary}</WorkSummary>}
|
|
428
|
+
</EducationItem>
|
|
429
|
+
))}
|
|
430
|
+
</Section>
|
|
431
|
+
)}
|
|
432
|
+
|
|
433
|
+
{languages?.length > 0 && (
|
|
434
|
+
<Section>
|
|
435
|
+
<StyledSectionTitle>Languages</StyledSectionTitle>
|
|
436
|
+
<TwoColumnGrid>
|
|
437
|
+
{languages.map((lang, index) => (
|
|
438
|
+
<CompactItem key={index}>
|
|
439
|
+
<CompactTitle>{lang.language}:</CompactTitle>{' '}
|
|
440
|
+
{lang.fluency || '—'}
|
|
441
|
+
</CompactItem>
|
|
442
|
+
))}
|
|
443
|
+
</TwoColumnGrid>
|
|
444
|
+
</Section>
|
|
445
|
+
)}
|
|
446
|
+
|
|
447
|
+
{interests?.length > 0 && (
|
|
448
|
+
<Section>
|
|
449
|
+
<StyledSectionTitle>Interests</StyledSectionTitle>
|
|
450
|
+
<TwoColumnGrid>
|
|
451
|
+
{interests.map((interest, index) => (
|
|
452
|
+
<CompactItem key={index}>
|
|
453
|
+
<CompactTitle>{interest.name}</CompactTitle>
|
|
454
|
+
{interest.keywords?.length > 0 && (
|
|
455
|
+
<>: {interest.keywords.join(', ')}</>
|
|
456
|
+
)}
|
|
457
|
+
</CompactItem>
|
|
458
|
+
))}
|
|
459
|
+
</TwoColumnGrid>
|
|
460
|
+
</Section>
|
|
461
|
+
)}
|
|
462
|
+
|
|
463
|
+
{references?.length > 0 && (
|
|
464
|
+
<Section>
|
|
465
|
+
<StyledSectionTitle>References</StyledSectionTitle>
|
|
466
|
+
{references.map((ref, index) => (
|
|
467
|
+
<EducationItem key={index}>
|
|
468
|
+
<Institution>{ref.name}</Institution>
|
|
469
|
+
{ref.reference && <WorkSummary>{ref.reference}</WorkSummary>}
|
|
470
|
+
</EducationItem>
|
|
471
|
+
))}
|
|
472
|
+
</Section>
|
|
473
|
+
)}
|
|
474
|
+
</Layout>
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
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: #f5f5f5;
|
|
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
|
+
}
|