kyd-shared-badge 0.3.110 → 0.3.111

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kyd-shared-badge",
3
- "version": "0.3.110",
3
+ "version": "0.3.111",
4
4
  "private": false,
5
5
  "main": "./src/index.ts",
6
6
  "module": "./src/index.ts",
@@ -190,7 +190,7 @@ const ReportHeader = ({ badgeId, developerName, updatedAt, score = 0, badgeImage
190
190
 
191
191
  {/* Badge Image with robust centered overlay */}
192
192
  <div className={`flex items-center ${rightBadgeLayout ? 'md:justify-end h-20' : 'justify-center w-full mt-4'} `}>
193
- <div className={`relative w-full ${rightBadgeLayout ? 'max-w-[85px]' : 'px-20'} select-none`}>
193
+ <div className={`relative w-full ${rightBadgeLayout ? 'max-w-[85px]' : 'px-28'} select-none`}>
194
194
  <Image
195
195
  src={finalBadgeImageUrl}
196
196
  alt="KYD Badge"
@@ -29,6 +29,14 @@ function asText(value: unknown): string {
29
29
  return String(value);
30
30
  }
31
31
 
32
+ function hasText(value: unknown): boolean {
33
+ return asText(value).trim().length > 0;
34
+ }
35
+
36
+ function hasAnyText(...values: unknown[]): boolean {
37
+ return values.some(hasText);
38
+ }
39
+
32
40
  const Row = ({ label, value }: { label: string; value?: string }) => {
33
41
  if (!value) return null;
34
42
  return (
@@ -54,6 +62,34 @@ export default function ResumeView({ resume, roleName, badgeId, isPublic, showDo
54
62
 
55
63
  const canDownload = !!(showDownloadButton && badgeId);
56
64
 
65
+ // Determine which sections have meaningful content and filter empty entries
66
+ const filteredSkillCategories = Array.isArray(skills?.categories)
67
+ ? (skills.categories as any[]).filter((cat: any) => Array.isArray(cat?.items) && cat.items.some(hasText))
68
+ : [];
69
+ const hasSkillsSection = filteredSkillCategories.length > 0;
70
+
71
+ const filteredExperience = experience.filter((exp: any) =>
72
+ hasAnyText(exp?.title, exp?.company, exp?.startDate, exp?.endDate) ||
73
+ (Array.isArray(exp?.highlights) && exp.highlights.some(hasText)) ||
74
+ (Array.isArray(exp?.technologies) && exp.technologies.some(hasText))
75
+ );
76
+ const hasExperienceSection = filteredExperience.length > 0;
77
+
78
+ const filteredProjects = projects.filter((p: any) =>
79
+ hasAnyText(p?.name, p?.description, p?.impact, p?.link) ||
80
+ (Array.isArray(p?.technologies) && p.technologies.some(hasText))
81
+ );
82
+ const hasProjectsSection = filteredProjects.length > 0;
83
+
84
+ const filteredEducation = education.filter((e: any) => hasAnyText(e?.degree, e?.institution, e?.graduationDate));
85
+ const hasEducationSection = filteredEducation.length > 0;
86
+
87
+ const filteredCertifications = certifications.filter((c: any) => hasAnyText(c?.name, c?.issuer, c?.date));
88
+ const hasCertificationsSection = filteredCertifications.length > 0;
89
+
90
+ const filteredLinks = links.filter((l: any) => hasAnyText(l?.label, l?.url));
91
+ const hasLinksSection = filteredLinks.length > 0;
92
+
57
93
  const handleDownloadResume = async () => {
58
94
  if (!badgeId) return;
59
95
  try {
@@ -113,22 +149,26 @@ export default function ResumeView({ resume, roleName, badgeId, isPublic, showDo
113
149
  </div>
114
150
  ) : null}
115
151
 
116
- {Array.isArray(skills?.categories) && skills.categories.length > 0 ? (
152
+ {hasSkillsSection ? (
117
153
  <div>
118
154
  <div className={'text-lg font-semibold mb-2'} style={{ color: 'var(--text-main)' }}>Skills</div>
119
155
  <div className={'space-y-1'}>
120
- {skills.categories.map((cat: any, idx: number) => (
121
- <Row key={idx} label={asText(cat?.name)} value={(Array.isArray(cat?.items) ? cat.items : []).join(', ')} />
156
+ {filteredSkillCategories.map((cat: any, idx: number) => (
157
+ <Row
158
+ key={idx}
159
+ label={asText(cat?.name)}
160
+ value={(Array.isArray(cat?.items) ? cat.items.filter(hasText) : []).join(', ')}
161
+ />
122
162
  ))}
123
163
  </div>
124
164
  </div>
125
165
  ) : null}
126
166
 
127
- {experience.length > 0 ? (
167
+ {hasExperienceSection ? (
128
168
  <div>
129
169
  <div className={'text-lg font-semibold mb-2'} style={{ color: 'var(--text-main)' }}>Experience</div>
130
170
  <div className={'space-y-4'}>
131
- {experience.map((exp: any, idx: number) => (
171
+ {filteredExperience.map((exp: any, idx: number) => (
132
172
  <div key={idx} className={'space-y-1'}>
133
173
  <div className={'font-semibold'} style={{ color: 'var(--text-main)' }}>
134
174
  {asText(exp?.title)}{asText(exp?.company) ? ` — ${asText(exp?.company)}` : ''}
@@ -136,15 +176,15 @@ export default function ResumeView({ resume, roleName, badgeId, isPublic, showDo
136
176
  <div className={'text-xs'} style={{ color: 'var(--text-secondary)' }}>
137
177
  {(asText(exp?.startDate) || asText(exp?.endDate)) ? `${asText(exp?.startDate)} – ${asText(exp?.endDate) || 'Present'}` : ''}
138
178
  </div>
139
- {Array.isArray(exp?.highlights) && exp.highlights.length > 0 ? (
179
+ {Array.isArray(exp?.highlights) && exp.highlights.filter(hasText).length > 0 ? (
140
180
  <ul className={'list-disc pl-5 text-sm'} style={{ color: 'var(--text-secondary)' }}>
141
- {exp.highlights.map((h: any, i: number) => (
181
+ {exp.highlights.filter(hasText).map((h: any, i: number) => (
142
182
  <li key={i}>{asText(h)}</li>
143
183
  ))}
144
184
  </ul>
145
185
  ) : null}
146
- {Array.isArray(exp?.technologies) && exp.technologies.length > 0 ? (
147
- <div className={'text-xs'} style={{ color: 'var(--text-secondary)' }}>Technologies: {exp.technologies.join(', ')}</div>
186
+ {Array.isArray(exp?.technologies) && exp.technologies.filter(hasText).length > 0 ? (
187
+ <div className={'text-xs'} style={{ color: 'var(--text-secondary)' }}>Technologies: {exp.technologies.filter(hasText).join(', ')}</div>
148
188
  ) : null}
149
189
  </div>
150
190
  ))}
@@ -152,17 +192,17 @@ export default function ResumeView({ resume, roleName, badgeId, isPublic, showDo
152
192
  </div>
153
193
  ) : null}
154
194
 
155
- {projects.length > 0 ? (
195
+ {hasProjectsSection ? (
156
196
  <div>
157
197
  <div className={'text-lg font-semibold mb-2'} style={{ color: 'var(--text-main)' }}>Projects</div>
158
198
  <div className={'space-y-4'}>
159
- {projects.map((p: any, idx: number) => (
199
+ {filteredProjects.map((p: any, idx: number) => (
160
200
  <div key={idx} className={'space-y-1'}>
161
201
  <div className={'font-semibold'} style={{ color: 'var(--text-main)' }}>{asText(p?.name)}</div>
162
202
  {asText(p?.description) ? <div className={'text-sm'} style={{ color: 'var(--text-secondary)' }}>{asText(p?.description)}</div> : null}
163
203
  {asText(p?.impact) ? <div className={'text-sm'} style={{ color: 'var(--text-secondary)' }}>Impact: {asText(p?.impact)}</div> : null}
164
- {Array.isArray(p?.technologies) && p.technologies.length > 0 ? (
165
- <div className={'text-xs'} style={{ color: 'var(--text-secondary)' }}>Technologies: {p.technologies.join(', ')}</div>
204
+ {Array.isArray(p?.technologies) && p.technologies.filter(hasText).length > 0 ? (
205
+ <div className={'text-xs'} style={{ color: 'var(--text-secondary)' }}>Technologies: {p.technologies.filter(hasText).join(', ')}</div>
166
206
  ) : null}
167
207
  {asText(p?.link) ? <div className={'text-xs'} style={{ color: 'var(--text-secondary)' }}>{asText(p?.link)}</div> : null}
168
208
  </div>
@@ -171,11 +211,11 @@ export default function ResumeView({ resume, roleName, badgeId, isPublic, showDo
171
211
  </div>
172
212
  ) : null}
173
213
 
174
- {education.length > 0 ? (
214
+ {hasEducationSection ? (
175
215
  <div>
176
216
  <div className={'text-lg font-semibold mb-2'} style={{ color: 'var(--text-main)' }}>Education</div>
177
217
  <div className={'space-y-2'}>
178
- {education.map((e: any, idx: number) => (
218
+ {filteredEducation.map((e: any, idx: number) => (
179
219
  <div key={idx} className={'text-sm'} style={{ color: 'var(--text-secondary)' }}>
180
220
  {`${asText(e?.degree)} — ${asText(e?.institution)}${asText(e?.graduationDate) ? ` (${asText(e?.graduationDate)})` : ''}`}
181
221
  </div>
@@ -184,11 +224,11 @@ export default function ResumeView({ resume, roleName, badgeId, isPublic, showDo
184
224
  </div>
185
225
  ) : null}
186
226
 
187
- {certifications.length > 0 ? (
227
+ {hasCertificationsSection ? (
188
228
  <div>
189
229
  <div className={'text-lg font-semibold mb-2'} style={{ color: 'var(--text-main)' }}>Certifications</div>
190
230
  <div className={'space-y-1'}>
191
- {certifications.map((c: any, idx: number) => (
231
+ {filteredCertifications.map((c: any, idx: number) => (
192
232
  <div key={idx} className={'text-sm'} style={{ color: 'var(--text-secondary)' }}>
193
233
  {`${asText(c?.name)} — ${asText(c?.issuer)}${asText(c?.date) ? ` (${asText(c?.date)})` : ''}`}
194
234
  </div>
@@ -197,11 +237,11 @@ export default function ResumeView({ resume, roleName, badgeId, isPublic, showDo
197
237
  </div>
198
238
  ) : null}
199
239
 
200
- {links.length > 0 ? (
240
+ {hasLinksSection ? (
201
241
  <div>
202
242
  <div className={'text-lg font-semibold mb-2'} style={{ color: 'var(--text-main)' }}>Links</div>
203
243
  <div className={'space-y-1'}>
204
- {links.map((l: any, idx: number) => (
244
+ {filteredLinks.map((l: any, idx: number) => (
205
245
  <div key={idx} className={'text-sm'} style={{ color: 'var(--text-secondary)' }}>
206
246
  {`${asText(l?.label)}: ${asText(l?.url)}`}
207
247
  </div>
@@ -62,8 +62,8 @@ export default function RoleOverviewCard({
62
62
  >
63
63
  <div className="mb-3 px-5 pt-5 flex items-start justify-between gap-2">
64
64
  <div>
65
- <div className={'font-semibold'} style={{ color: 'var(--text-main)' }}>{title}</div>
66
- <div className={'text-xs mt-1'} style={{ color: 'var(--text-secondary)' }}>How well the candidate aligns with the target role based on KYD evidence.</div>
65
+ <div className={'font-semibold text-xl'} style={{ color: 'var(--text-main)' }}>{title}</div>
66
+ <div className={'text-sm mt-1'} style={{ color: 'var(--text-secondary)' }}>How well the candidate aligns with the target role based on KYD evidence.</div>
67
67
  </div>
68
68
  <span className={'relative inline-flex items-center group cursor-help'} style={{ color: 'var(--text-secondary)' }}>
69
69
  <FiInfo />
@@ -408,12 +408,12 @@ export default function SkillsBubble({ skillsCategoryRadar, skillsByCategory, sk
408
408
  color: 'var(--text-secondary)'
409
409
  }}
410
410
  >
411
- <div className="flex items-center gap-2">
412
- <span className="inline-block h-3 w-3 rounded-full" style={{ background: green5 }} />
411
+ <div className="flex items-center gap-2 text-base">
412
+ <span className="inline-block h-3 w-3 rounded-full border border-[var(--text-secondary)]" style={{ background: green5 }} />
413
413
  <span>Size = evidence count per category</span>
414
414
  </div>
415
- <div className="flex items-center gap-2 mt-1">
416
- <span className="inline-block h-3 w-3 rounded-full border border-[var(--text-secondary)]" style={{ background: green1 }} />
415
+ <div className="flex items-center gap-2 mt-1 text-base">
416
+ <span className="inline-block h-3 w-3 rounded-full" style={{ background: green1 }} />
417
417
  <span>Color = experience (darker = more)</span>
418
418
  </div>
419
419
  </div>