kamisaku 0.3.3 → 0.4.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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +17 -0
  3. data/Gemfile.lock +1 -1
  4. data/README.md +28 -64
  5. data/examples/birthday_invitation/dino/invitation.pdf +0 -0
  6. data/examples/resume/chromatic/example.pdf +0 -0
  7. data/examples/resume/gradient/example.pdf +0 -0
  8. data/examples/resume/meridian/example.pdf +0 -0
  9. data/examples/resume/paper/example.pdf +0 -0
  10. data/examples/resume/prism/example.pdf +0 -0
  11. data/examples/resume/sleek/example.pdf +0 -0
  12. data/examples/resume/zenith/example.pdf +0 -0
  13. data/lib/kamisaku/arg_parser.rb +4 -0
  14. data/lib/kamisaku/cli_runner.rb +2 -2
  15. data/lib/kamisaku/content_validators/base_content_validator.rb +14 -0
  16. data/lib/kamisaku/content_validators/birthday_invitation_content_validator.rb +218 -0
  17. data/lib/kamisaku/{content_validator.rb → content_validators/resume_content_validator.rb} +28 -11
  18. data/lib/kamisaku/html_builder.rb +4 -3
  19. data/lib/kamisaku/pdf.rb +17 -12
  20. data/lib/kamisaku/template_helpers.rb +0 -2
  21. data/lib/kamisaku/version.rb +1 -1
  22. data/lib/kamisaku.rb +4 -2
  23. data/lib/schema/birthday_invitation/example.yml +45 -0
  24. data/lib/schema/birthday_invitation/schema.yml +40 -0
  25. data/lib/schema/resume/example.yml +274 -0
  26. data/lib/schema/resume/schema.yml +112 -0
  27. data/lib/templates/birthday_invitation/dino/template.html.erb +486 -0
  28. data/lib/templates/resume/chromatic/template.html.erb +275 -0
  29. data/lib/templates/resume/gradient/template.html.erb +793 -0
  30. data/lib/templates/resume/meridian/template.html.erb +535 -0
  31. data/lib/templates/resume/paper/template.html.erb +525 -0
  32. data/lib/templates/resume/prism/template.html.erb +818 -0
  33. data/lib/templates/{sleek → resume/sleek}/template.html.erb +1 -1
  34. data/lib/templates/resume/zenith/template.html.erb +546 -0
  35. data/scripts/rebuild_examples.rb +45 -0
  36. metadata +25 -9
  37. data/examples/paper/john_doe.pdf +0 -0
  38. data/examples/paper/john_doe.yml +0 -157
  39. data/examples/sleek/john_doe.pdf +0 -0
  40. data/examples/sleek/john_doe.yml +0 -157
  41. data/lib/templates/paper/template.html.erb +0 -420
  42. data/template.yml +0 -64
@@ -0,0 +1,793 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title><%= data[:profile][:name] %> - Resume</title>
7
+ <style>
8
+ /* Base print and preview styles */
9
+ @page {
10
+ margin: 0 auto;
11
+ size: A4;
12
+ }
13
+
14
+ @media screen {
15
+ body {
16
+ background: #e0e0e0;
17
+ }
18
+ }
19
+
20
+ @media screen, print {
21
+ .paper {
22
+ background: white;
23
+ margin: 0 auto;
24
+ width: 210mm;
25
+ height: 100%;
26
+ }
27
+ }
28
+
29
+ /* Creative styling with vibrant colors */
30
+ * {
31
+ margin: 0;
32
+ padding: 0;
33
+ box-sizing: border-box;
34
+ }
35
+
36
+ body {
37
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
38
+ line-height: 1.6;
39
+ color: #333;
40
+ }
41
+
42
+ .resume-container {
43
+ display: grid;
44
+ grid-template-columns: 35% 65%;
45
+ min-height: 100vh;
46
+ }
47
+
48
+ /* Left sidebar with vibrant gradient */
49
+ .sidebar {
50
+ background: linear-gradient(135deg, #ff6b6b 0%, #4ecdc4 50%, #45b7d1 100%);
51
+ color: white;
52
+ padding: 2rem;
53
+ }
54
+
55
+ .profile-section {
56
+ text-align: center;
57
+ margin-bottom: 2rem;
58
+ }
59
+
60
+ .profile-name {
61
+ font-size: 1.8rem;
62
+ font-weight: bold;
63
+ margin-bottom: 0.5rem;
64
+ text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
65
+ }
66
+
67
+ .profile-title {
68
+ font-size: 1.1rem;
69
+ opacity: 0.95;
70
+ margin-bottom: 1rem;
71
+ background: rgba(255,255,255,0.2);
72
+ padding: 0.5rem 1rem;
73
+ border-radius: 20px;
74
+ display: inline-block;
75
+ }
76
+
77
+ .profile-about {
78
+ font-size: 0.9rem;
79
+ line-height: 1.5;
80
+ opacity: 0.95;
81
+ background: rgba(255,255,255,0.1);
82
+ padding: 1rem;
83
+ border-radius: 15px;
84
+ }
85
+
86
+ .sidebar-section {
87
+ margin-bottom: 2rem;
88
+ }
89
+
90
+ .sidebar-heading {
91
+ font-size: 1.2rem;
92
+ font-weight: bold;
93
+ margin-bottom: 1rem;
94
+ display: flex;
95
+ align-items: center;
96
+ gap: 0.5rem;
97
+ background: rgba(255,255,255,0.2);
98
+ padding: 0.7rem 1rem;
99
+ border-radius: 25px;
100
+ }
101
+
102
+ .contact-item {
103
+ display: flex;
104
+ align-items: center;
105
+ gap: 0.7rem;
106
+ margin-bottom: 0.7rem;
107
+ font-size: 0.9rem;
108
+ background: rgba(255,255,255,0.1);
109
+ padding: 0.5rem 1rem;
110
+ border-radius: 15px;
111
+ transition: all 0.3s ease;
112
+ }
113
+
114
+ .contact-item:hover {
115
+ background: rgba(255,255,255,0.2);
116
+ transform: translateX(5px);
117
+ }
118
+
119
+ .skill-category {
120
+ margin-bottom: 1.5rem;
121
+ background: rgba(255,255,255,0.1);
122
+ padding: 1rem;
123
+ border-radius: 15px;
124
+ }
125
+
126
+ .skill-name {
127
+ font-weight: 600;
128
+ margin-bottom: 0.5rem;
129
+ font-size: 1rem;
130
+ display: flex;
131
+ align-items: center;
132
+ gap: 0.5rem;
133
+ }
134
+
135
+ .skill-items {
136
+ list-style: none;
137
+ padding-left: 1rem;
138
+ }
139
+
140
+ .skill-items li {
141
+ position: relative;
142
+ margin-bottom: 0.3rem;
143
+ font-size: 0.85rem;
144
+ padding: 0.2rem 0;
145
+ }
146
+
147
+ .skill-items li:before {
148
+ content: "🔸";
149
+ position: absolute;
150
+ left: -1rem;
151
+ }
152
+
153
+ .language-item, .interest-item {
154
+ background: linear-gradient(45deg, #ff9a9e, #fecfef);
155
+ color: #333;
156
+ padding: 0.4rem 1rem;
157
+ border-radius: 20px;
158
+ display: inline-block;
159
+ margin: 0.3rem 0.3rem 0.3rem 0;
160
+ font-size: 0.85rem;
161
+ font-weight: 500;
162
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
163
+ }
164
+
165
+ /* Main content */
166
+ .main-content {
167
+ padding: 2rem;
168
+ background: linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%);
169
+ min-height: 100vh;
170
+ }
171
+
172
+ .section {
173
+ margin-bottom: 2.5rem;
174
+ }
175
+
176
+ .section-heading {
177
+ color: #2c3e50;
178
+ font-size: 1.4rem;
179
+ font-weight: bold;
180
+ margin-bottom: 1.5rem;
181
+ display: flex;
182
+ align-items: center;
183
+ gap: 0.7rem;
184
+ background: linear-gradient(45deg, #667eea, #764ba2);
185
+ color: white;
186
+ padding: 1rem 1.5rem;
187
+ border-radius: 25px;
188
+ box-shadow: 0 4px 8px rgba(0,0,0,0.2);
189
+ }
190
+
191
+ .experience-item, .education-item, .project-item, .certification-item,
192
+ .award-item, .publication-item, .conference-item, .volunteering-item {
193
+ margin-bottom: 2rem;
194
+ padding: 1.5rem;
195
+ background: white;
196
+ border-radius: 20px;
197
+ box-shadow: 0 6px 12px rgba(0,0,0,0.1);
198
+ border-left: 6px solid #ff6b6b;
199
+ position: relative;
200
+ transition: all 0.3s ease;
201
+ }
202
+
203
+ .experience-item:hover, .education-item:hover, .project-item:hover,
204
+ .certification-item:hover, .award-item:hover, .publication-item:hover,
205
+ .conference-item:hover, .volunteering-item:hover {
206
+ transform: translateY(-2px);
207
+ box-shadow: 0 8px 16px rgba(0,0,0,0.15);
208
+ }
209
+
210
+ .experience-item { border-left-color: #ff6b6b; }
211
+ .education-item { border-left-color: #4ecdc4; }
212
+ .project-item { border-left-color: #45b7d1; }
213
+ .certification-item { border-left-color: #96ceb4; }
214
+ .award-item { border-left-color: #feca57; }
215
+ .publication-item { border-left-color: #ff9ff3; }
216
+ .conference-item { border-left-color: #54a0ff; }
217
+ .volunteering-item { border-left-color: #5f27cd; }
218
+
219
+ .item-header {
220
+ display: flex;
221
+ justify-content: space-between;
222
+ align-items: flex-start;
223
+ margin-bottom: 1rem;
224
+ flex-wrap: wrap;
225
+ }
226
+
227
+ .item-title {
228
+ font-size: 1.1rem;
229
+ font-weight: bold;
230
+ color: #2c3e50;
231
+ margin-bottom: 0.3rem;
232
+ display: flex;
233
+ align-items: center;
234
+ gap: 0.5rem;
235
+ }
236
+
237
+ .item-organization, .item-institute, .item-issuer {
238
+ color: #e74c3c;
239
+ font-weight: 600;
240
+ margin-bottom: 0.3rem;
241
+ display: flex;
242
+ align-items: center;
243
+ gap: 0.5rem;
244
+ }
245
+
246
+ .item-location {
247
+ color: #7f8c8d;
248
+ font-size: 0.9rem;
249
+ display: flex;
250
+ align-items: center;
251
+ gap: 0.3rem;
252
+ }
253
+
254
+ .item-date {
255
+ background: linear-gradient(45deg, #ff6b6b, #4ecdc4);
256
+ color: white;
257
+ padding: 0.5rem 1rem;
258
+ border-radius: 20px;
259
+ font-size: 0.8rem;
260
+ white-space: nowrap;
261
+ font-weight: 600;
262
+ box-shadow: 0 2px 4px rgba(0,0,0,0.2);
263
+ }
264
+
265
+ .item-description {
266
+ margin-bottom: 1rem;
267
+ color: #555;
268
+ line-height: 1.6;
269
+ padding: 1rem;
270
+ background: linear-gradient(45deg, #f8f9fa, #e9ecef);
271
+ border-radius: 10px;
272
+ }
273
+
274
+ .item-skills, .item-technologies {
275
+ display: flex;
276
+ flex-wrap: wrap;
277
+ gap: 0.5rem;
278
+ margin-bottom: 1rem;
279
+ }
280
+
281
+ .skill-tag {
282
+ background: linear-gradient(45deg, #ff6b6b, #ff8e8e);
283
+ color: white;
284
+ padding: 0.3rem 0.8rem;
285
+ border-radius: 15px;
286
+ font-size: 0.8rem;
287
+ font-weight: 500;
288
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
289
+ }
290
+
291
+ .tech-tag {
292
+ background: linear-gradient(45deg, #4ecdc4, #44a08d);
293
+ color: white;
294
+ padding: 0.3rem 0.8rem;
295
+ border-radius: 15px;
296
+ font-size: 0.8rem;
297
+ font-weight: 500;
298
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
299
+ }
300
+
301
+ .item-achievements {
302
+ list-style: none;
303
+ padding: 0;
304
+ }
305
+
306
+ .item-achievements li {
307
+ position: relative;
308
+ padding-left: 2rem;
309
+ margin-bottom: 0.7rem;
310
+ color: #555;
311
+ line-height: 1.5;
312
+ }
313
+
314
+ .item-achievements li:before {
315
+ content: "🚀";
316
+ position: absolute;
317
+ left: 0;
318
+ font-size: 1rem;
319
+ }
320
+
321
+ .project-link {
322
+ color: #e74c3c;
323
+ text-decoration: none;
324
+ font-weight: 600;
325
+ display: inline-flex;
326
+ align-items: center;
327
+ gap: 0.3rem;
328
+ background: linear-gradient(45deg, #ffecd2, #fcb69f);
329
+ padding: 0.5rem 1rem;
330
+ border-radius: 15px;
331
+ transition: all 0.3s ease;
332
+ }
333
+
334
+ .project-link:hover {
335
+ background: linear-gradient(45deg, #fcb69f, #ffecd2);
336
+ transform: translateY(-1px);
337
+ box-shadow: 0 4px 8px rgba(0,0,0,0.1);
338
+ }
339
+
340
+ /* ATS-friendly text elements */
341
+ .ats-text {
342
+ font-size: 0;
343
+ line-height: 0;
344
+ height: 0;
345
+ overflow: hidden;
346
+ }
347
+ </style>
348
+ </head>
349
+ <body>
350
+ <div class="paper">
351
+ <div class="resume-container">
352
+ <!-- Left Sidebar -->
353
+ <div class="sidebar">
354
+ <!-- Profile Section -->
355
+ <div class="profile-section">
356
+ <h1 class="profile-name">👋 <%= data[:profile][:name] %></h1>
357
+ <div class="ats-text">Name: <%= data[:profile][:name] %></div>
358
+
359
+ <% if data[:profile][:title] %>
360
+ <div class="profile-title">💼 <%= data[:profile][:title] %></div>
361
+ <div class="ats-text">Title: <%= data[:profile][:title] %></div>
362
+ <% end %>
363
+
364
+ <% if data[:profile][:about] %>
365
+ <div class="profile-about">✨ <%= data[:profile][:about] %></div>
366
+ <% end %>
367
+ </div>
368
+
369
+ <!-- Contact Information -->
370
+ <% if data[:contact] %>
371
+ <div class="sidebar-section">
372
+ <h2 class="sidebar-heading">
373
+ 📞 Contact
374
+ </h2>
375
+
376
+ <% if data[:contact][:email] %>
377
+ <div class="contact-item">
378
+ 📧 <span><%= data[:contact][:email] %></span>
379
+ </div>
380
+ <div class="ats-text">Email: <%= data[:contact][:email] %></div>
381
+ <% end %>
382
+
383
+ <% if data[:contact][:mobile] %>
384
+ <div class="contact-item">
385
+ 📱 <span><%= data[:contact][:mobile] %></span>
386
+ </div>
387
+ <div class="ats-text">Phone: <%= data[:contact][:mobile] %></div>
388
+ <% end %>
389
+
390
+ <% if data[:contact][:location] && (data[:contact][:location][:city] || data[:contact][:location][:country]) %>
391
+ <div class="contact-item">
392
+ 📍 <span>
393
+ <%= [data[:contact][:location][:city], data[:contact][:location][:country]].compact.join(', ') %>
394
+ </span>
395
+ </div>
396
+ <% end %>
397
+
398
+ <% if data[:contact][:linkedin] %>
399
+ <div class="contact-item">
400
+ 💼 <span>linkedin.com/in/<%= data[:contact][:linkedin] %></span>
401
+ </div>
402
+ <div class="ats-text">LinkedIn: <%= data[:contact][:linkedin] %></div>
403
+ <% end %>
404
+
405
+ <% if data[:contact][:github] %>
406
+ <div class="contact-item">
407
+ 🐙 <span>github.com/<%= data[:contact][:github] %></span>
408
+ </div>
409
+ <div class="ats-text">GitHub: <%= data[:contact][:github] %></div>
410
+ <% end %>
411
+ </div>
412
+ <% end %>
413
+
414
+ <!-- Skills -->
415
+ <% if data[:skills] && !data[:skills].empty? %>
416
+ <div class="sidebar-section">
417
+ <h2 class="sidebar-heading">
418
+ 🛠️ Skills
419
+ </h2>
420
+
421
+ <% data[:skills].each do |skill| %>
422
+ <div class="skill-category">
423
+ <div class="skill-name">⚡ <%= skill[:name] %></div>
424
+ <div class="ats-text">Skill: <%= skill[:name] %></div>
425
+
426
+ <% if skill[:items] && !skill[:items].empty? %>
427
+ <ul class="skill-items">
428
+ <% skill[:items].each do |item| %>
429
+ <li><%= item %></li>
430
+ <div class="ats-text">Skill Item: <%= item %></div>
431
+ <% end %>
432
+ </ul>
433
+ <% end %>
434
+ </div>
435
+ <% end %>
436
+ </div>
437
+ <% end %>
438
+
439
+ <!-- Languages -->
440
+ <% if data[:languages] && !data[:languages].empty? %>
441
+ <div class="sidebar-section">
442
+ <h2 class="sidebar-heading">
443
+ 🗣️ Languages
444
+ </h2>
445
+
446
+ <% data[:languages].each do |language| %>
447
+ <span class="language-item">🌐 <%= language[:name] %></span>
448
+ <div class="ats-text">Language: <%= language[:name] %></div>
449
+ <% end %>
450
+ </div>
451
+ <% end %>
452
+
453
+ <!-- Interests -->
454
+ <% if data[:interests] && !data[:interests].empty? %>
455
+ <div class="sidebar-section">
456
+ <h2 class="sidebar-heading">
457
+ ❤️ Interests
458
+ </h2>
459
+
460
+ <% data[:interests].each do |interest| %>
461
+ <span class="interest-item">🎯 <%= interest[:name] %></span>
462
+ <div class="ats-text">Interest: <%= interest[:name] %></div>
463
+ <% end %>
464
+ </div>
465
+ <% end %>
466
+ </div>
467
+
468
+ <!-- Main Content -->
469
+ <div class="main-content">
470
+ <!-- Experience -->
471
+ <% if data[:experiences] && !data[:experiences].empty? %>
472
+ <div class="section">
473
+ <h2 class="section-heading">
474
+ 💼 Experience
475
+ </h2>
476
+
477
+ <% data[:experiences].each do |experience| %>
478
+ <div class="experience-item">
479
+ <div class="item-header">
480
+ <div>
481
+ <div class="item-title">🎯 <%= experience[:title] %></div>
482
+ <div class="ats-text">Job Title: <%= experience[:title] %></div>
483
+
484
+ <div class="item-organization">🏢 <%= experience[:organisation] %></div>
485
+ <div class="ats-text">Company: <%= experience[:organisation] %></div>
486
+
487
+ <% if experience[:location] && (experience[:location][:city] || experience[:location][:country]) %>
488
+ <div class="item-location">
489
+ 📍 <%= [experience[:location][:city], experience[:location][:country]].compact.join(', ') %>
490
+ </div>
491
+ <% end %>
492
+ </div>
493
+
494
+ <div class="item-date">
495
+ 📅 <%= Date.new(experience[:from][:year], experience[:from][:month]).strftime('%b %Y') %> -
496
+ <%= experience[:to] ? Date.new(experience[:to][:year], experience[:to][:month]).strftime('%b %Y') : 'Present' %>
497
+ </div>
498
+ </div>
499
+
500
+ <% if experience[:skills] && !experience[:skills].empty? %>
501
+ <div class="item-skills">
502
+ <% experience[:skills].each do |skill| %>
503
+ <span class="skill-tag">⚡ <%= skill %></span>
504
+ <div class="ats-text">Experience Skill: <%= skill %></div>
505
+ <% end %>
506
+ </div>
507
+ <% end %>
508
+
509
+ <% if experience[:achievements] && !experience[:achievements].empty? %>
510
+ <ul class="item-achievements">
511
+ <% experience[:achievements].each do |achievement| %>
512
+ <li><%= achievement %></li>
513
+ <div class="ats-text">Achievement: <%= achievement %></div>
514
+ <% end %>
515
+ </ul>
516
+ <% end %>
517
+ </div>
518
+ <% end %>
519
+ </div>
520
+ <% end %>
521
+
522
+ <!-- Education -->
523
+ <% if data[:education] && !data[:education].empty? %>
524
+ <div class="section">
525
+ <h2 class="section-heading">
526
+ 🎓 Education
527
+ </h2>
528
+
529
+ <% data[:education].each do |education| %>
530
+ <div class="education-item">
531
+ <div class="item-header">
532
+ <div>
533
+ <div class="item-title">📜 <%= education[:qualification] %></div>
534
+ <div class="ats-text">Degree: <%= education[:qualification] %></div>
535
+
536
+ <% if education[:field] %>
537
+ <div class="item-title">📚 <%= education[:field] %></div>
538
+ <div class="ats-text">Field of Study: <%= education[:field] %></div>
539
+ <% end %>
540
+
541
+ <div class="item-institute">🏫 <%= education[:institute] %></div>
542
+ <div class="ats-text">School: <%= education[:institute] %></div>
543
+
544
+ <% if education[:location] && (education[:location][:city] || education[:location][:country]) %>
545
+ <div class="item-location">
546
+ 📍 <%= [education[:location][:city], education[:location][:country]].compact.join(', ') %>
547
+ </div>
548
+ <% end %>
549
+ </div>
550
+
551
+ <% if education[:from] %>
552
+ <div class="item-date">
553
+ 📅 <%= Date.new(education[:from][:year], education[:from][:month]).strftime('%b %Y') %> -
554
+ <%= education[:to] ? Date.new(education[:to][:year], education[:to][:month]).strftime('%b %Y') : 'Present' %>
555
+ </div>
556
+ <% end %>
557
+ </div>
558
+
559
+ <% if education[:achievements] && !education[:achievements].empty? %>
560
+ <ul class="item-achievements">
561
+ <% education[:achievements].each do |achievement| %>
562
+ <li><%= achievement %></li>
563
+ <div class="ats-text">Education Achievement: <%= achievement %></div>
564
+ <% end %>
565
+ </ul>
566
+ <% end %>
567
+ </div>
568
+ <% end %>
569
+ </div>
570
+ <% end %>
571
+
572
+ <!-- Projects -->
573
+ <% if data[:projects] && !data[:projects].empty? %>
574
+ <div class="section">
575
+ <h2 class="section-heading">
576
+ 🚀 Projects
577
+ </h2>
578
+
579
+ <% data[:projects].each do |project| %>
580
+ <div class="project-item">
581
+ <div class="item-header">
582
+ <div>
583
+ <div class="item-title">💡 <%= project[:name] %></div>
584
+ <div class="ats-text">Project: <%= project[:name] %></div>
585
+
586
+ <% if project[:link] %>
587
+ <a href="<%= project[:link] %>" class="project-link" target="_blank">
588
+ 🔗 View Project
589
+ </a>
590
+ <% end %>
591
+ </div>
592
+ </div>
593
+
594
+ <% if project[:description] %>
595
+ <div class="item-description">📝 <%= project[:description] %></div>
596
+ <% end %>
597
+
598
+ <% if project[:technologies] && !project[:technologies].empty? %>
599
+ <div class="item-technologies">
600
+ <% project[:technologies].each do |tech| %>
601
+ <span class="tech-tag">🔧 <%= tech %></span>
602
+ <div class="ats-text">Technology: <%= tech %></div>
603
+ <% end %>
604
+ </div>
605
+ <% end %>
606
+ </div>
607
+ <% end %>
608
+ </div>
609
+ <% end %>
610
+
611
+ <!-- Certifications -->
612
+ <% if data[:certifications] && !data[:certifications].empty? %>
613
+ <div class="section">
614
+ <h2 class="section-heading">
615
+ 🏆 Certifications
616
+ </h2>
617
+
618
+ <% data[:certifications].each do |cert| %>
619
+ <div class="certification-item">
620
+ <div class="item-header">
621
+ <div>
622
+ <div class="item-title">🏅 <%= cert[:name] %></div>
623
+ <div class="ats-text">Certification: <%= cert[:name] %></div>
624
+
625
+ <% if cert[:issuer] %>
626
+ <div class="item-issuer">🏛️ <%= cert[:issuer] %></div>
627
+ <div class="ats-text">Issuer: <%= cert[:issuer] %></div>
628
+ <% end %>
629
+ </div>
630
+
631
+ <% if cert[:date] %>
632
+ <div class="item-date">
633
+ 📅 <%= Date.new(cert[:date][:year], cert[:date][:month], cert[:date][:day]).strftime('%b %Y') %>
634
+ </div>
635
+ <% end %>
636
+ </div>
637
+
638
+ <% if cert[:credential_url] %>
639
+ <a href="<%= cert[:credential_url] %>" class="project-link" target="_blank">
640
+ 🔗 View Credential
641
+ </a>
642
+ <% end %>
643
+ </div>
644
+ <% end %>
645
+ </div>
646
+ <% end %>
647
+
648
+ <!-- Awards -->
649
+ <% if data[:awards] && !data[:awards].empty? %>
650
+ <div class="section">
651
+ <h2 class="section-heading">
652
+ 🥇 Awards
653
+ </h2>
654
+
655
+ <% data[:awards].each do |award| %>
656
+ <div class="award-item">
657
+ <div class="item-header">
658
+ <div>
659
+ <div class="item-title">🏆 <%= award[:title] %></div>
660
+ <div class="ats-text">Award: <%= award[:title] %></div>
661
+
662
+ <% if award[:issuer] %>
663
+ <div class="item-issuer">🏛️ <%= award[:issuer] %></div>
664
+ <div class="ats-text">Award Issuer: <%= award[:issuer] %></div>
665
+ <% end %>
666
+ </div>
667
+
668
+ <% if award[:date] %>
669
+ <div class="item-date">
670
+ 📅 <%= Date.new(award[:date][:year], award[:date][:month], award[:date][:day]).strftime('%b %Y') %>
671
+ </div>
672
+ <% end %>
673
+ </div>
674
+
675
+ <% if award[:description] %>
676
+ <div class="item-description">📝 <%= award[:description] %></div>
677
+ <% end %>
678
+ </div>
679
+ <% end %>
680
+ </div>
681
+ <% end %>
682
+
683
+ <!-- Publications -->
684
+ <% if data[:publications] && !data[:publications].empty? %>
685
+ <div class="section">
686
+ <h2 class="section-heading">
687
+ 📖 Publications
688
+ </h2>
689
+
690
+ <% data[:publications].each do |publication| %>
691
+ <div class="publication-item">
692
+ <div class="item-header">
693
+ <div>
694
+ <div class="item-title">📄 <%= publication[:title] %></div>
695
+ <div class="ats-text">Publication: <%= publication[:title] %></div>
696
+
697
+ <% if publication[:publisher] %>
698
+ <div class="item-organization">🏢 <%= publication[:publisher] %></div>
699
+ <div class="ats-text">Publisher: <%= publication[:publisher] %></div>
700
+ <% end %>
701
+ </div>
702
+
703
+ <% if publication[:date] %>
704
+ <div class="item-date">
705
+ 📅 <%= Date.new(publication[:date][:year], publication[:date][:month], publication[:date][:day]).strftime('%b %Y') %>
706
+ </div>
707
+ <% end %>
708
+ </div>
709
+
710
+ <% if publication[:link] %>
711
+ <a href="<%= publication[:link] %>" class="project-link" target="_blank">
712
+ 🔗 View Publication
713
+ </a>
714
+ <% end %>
715
+ </div>
716
+ <% end %>
717
+ </div>
718
+ <% end %>
719
+
720
+ <!-- Conferences -->
721
+ <% if data[:conferences] && !data[:conferences].empty? %>
722
+ <div class="section">
723
+ <h2 class="section-heading">
724
+ 🎤 Conferences
725
+ </h2>
726
+
727
+ <% data[:conferences].each do |conference| %>
728
+ <div class="conference-item">
729
+ <div class="item-header">
730
+ <div>
731
+ <div class="item-title">🎪 <%= conference[:name] %></div>
732
+ <div class="ats-text">Conference: <%= conference[:name] %></div>
733
+
734
+ <% if conference[:role] %>
735
+ <div class="item-organization">🎭 <%= conference[:role] %></div>
736
+ <div class="ats-text">Role: <%= conference[:role] %></div>
737
+ <% end %>
738
+ </div>
739
+
740
+ <% if conference[:date] %>
741
+ <div class="item-date">
742
+ 📅 <%= Date.new(conference[:date][:year], conference[:date][:month], conference[:date][:day]).strftime('%b %Y') %>
743
+ </div>
744
+ <% end %>
745
+ </div>
746
+
747
+ <% if conference[:description] %>
748
+ <div class="item-description">📝 <%= conference[:description] %></div>
749
+ <% end %>
750
+ </div>
751
+ <% end %>
752
+ </div>
753
+ <% end %>
754
+
755
+ <!-- Volunteering -->
756
+ <% if data[:volunteering] && !data[:volunteering].empty? %>
757
+ <div class="section">
758
+ <h2 class="section-heading">
759
+ 🤝 Volunteering
760
+ </h2>
761
+
762
+ <% data[:volunteering].each do |volunteer| %>
763
+ <div class="volunteering-item">
764
+ <div class="item-header">
765
+ <div>
766
+ <div class="item-title">❤️ <%= volunteer[:organization] %></div>
767
+ <div class="ats-text">Volunteer Organization: <%= volunteer[:organization] %></div>
768
+
769
+ <% if volunteer[:role] %>
770
+ <div class="item-organization">🎭 <%= volunteer[:role] %></div>
771
+ <div class="ats-text">Volunteer Role: <%= volunteer[:role] %></div>
772
+ <% end %>
773
+ </div>
774
+
775
+ <% if volunteer[:date] %>
776
+ <div class="item-date">
777
+ 📅 <%= Date.new(volunteer[:date][:year], volunteer[:date][:month], volunteer[:date][:day]).strftime('%b %Y') %>
778
+ </div>
779
+ <% end %>
780
+ </div>
781
+
782
+ <% if volunteer[:description] %>
783
+ <div class="item-description">📝 <%= volunteer[:description] %></div>
784
+ <% end %>
785
+ </div>
786
+ <% end %>
787
+ </div>
788
+ <% end %>
789
+ </div>
790
+ </div>
791
+ </div>
792
+ </body>
793
+ </html>