kyd-shared-badge 0.3.208 → 0.3.210
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,38 +1,41 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { FiAlertTriangle } from 'react-icons/fi';
|
|
4
|
-
import {
|
|
4
|
+
import { EnterpriseMatch, GraphInsightsPayload, PublicBadgeData, ScoringSummary, TopBusinessRule } from './types';
|
|
5
5
|
// import ShareButton from './components/ShareButton';
|
|
6
|
-
import ReportHeader from './components/ReportHeader';
|
|
7
6
|
import EnterpriseCoaching from './components/EnterpriseCoaching';
|
|
8
|
-
import AppendixTables from './components/AppendixTables';
|
|
9
|
-
import BusinessRuleLink from './components/BusinessRuleLink';
|
|
10
7
|
import IpRiskAnalysisDisplay from './components/IpRiskAnalysisDisplay';
|
|
8
|
+
import ReportHeader from './components/ReportHeader';
|
|
11
9
|
// import Image from 'next/image';
|
|
12
|
-
import GraphInsights from './components/GraphInsights';
|
|
13
10
|
import ConnectedPlatforms from './components/ConnectedPlatforms';
|
|
14
|
-
import { FaGithub, FaGitlab, FaStackOverflow, FaLinkedin, FaGoogle, FaKaggle } from 'react-icons/fa';
|
|
15
|
-
import { SiCredly, SiFiverr } from 'react-icons/si';
|
|
16
11
|
import GaugeCard from './components/GaugeCard';
|
|
12
|
+
import GraphInsights from './components/GraphInsights';
|
|
17
13
|
import RiskCard from './components/RiskCard';
|
|
18
14
|
import RoleOverviewCard from './components/RoleOverviewCard';
|
|
19
15
|
|
|
20
|
-
import
|
|
21
|
-
import
|
|
22
|
-
import SkillsBubble from './components/SkillsBubble';
|
|
23
|
-
import CategoryBars from './components/CategoryBars';
|
|
24
|
-
import SkillsAppendixTable from './components/SkillsAppendixTable';
|
|
16
|
+
import ChatWidget from './chat/ChatWidget';
|
|
17
|
+
import { yellow } from './colors';
|
|
25
18
|
import { BusinessRulesProvider } from './components/BusinessRulesContext';
|
|
19
|
+
import CategoryBars from './components/CategoryBars';
|
|
26
20
|
import Reveal from './components/Reveal';
|
|
27
|
-
import
|
|
28
|
-
import
|
|
29
|
-
import UseCases from './components/UseCases';
|
|
30
|
-
import SummaryCards from './components/SummaryCards';
|
|
21
|
+
import SkillsBubble from './components/SkillsBubble';
|
|
22
|
+
import SkillsValidation from './components/SkillsValidation';
|
|
31
23
|
import TopContributingFactors from './components/TopContributingFactors';
|
|
24
|
+
import UseCases from './components/UseCases';
|
|
25
|
+
import { formatLocalDateTime } from './utils/date';
|
|
32
26
|
// import AiUsageBody from './components/AiUsageBody';
|
|
33
|
-
import SanctionsMatches from './components/SanctionsMatches';
|
|
34
27
|
import AppendixContent from './components/AppendixContent';
|
|
35
|
-
import {
|
|
28
|
+
import { getCategoryTooltipCopy } from './utils/provider';
|
|
29
|
+
|
|
30
|
+
const DEFAULT_AI_CATEGORY_ORDER = [
|
|
31
|
+
'Ecosystems & Providers',
|
|
32
|
+
'Orchestration & Dev',
|
|
33
|
+
'Specialized AI',
|
|
34
|
+
'Machine Learning',
|
|
35
|
+
'Infrastructure',
|
|
36
|
+
'Governance',
|
|
37
|
+
] as const;
|
|
38
|
+
|
|
36
39
|
type ChatWidgetProps = Partial<{
|
|
37
40
|
api: string;
|
|
38
41
|
title: string;
|
|
@@ -80,6 +83,12 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless }: { badgeData: Pub
|
|
|
80
83
|
const genreMapping = (scoringSummary?.config?.genre_mapping) || {};
|
|
81
84
|
const categoryScores = (scoringSummary?.category_scores) || {};
|
|
82
85
|
const categoryTopByGraph = (graphInsights?.categoryTopBusiness) || {};
|
|
86
|
+
const aiCategoryOrder =
|
|
87
|
+
graphInsights?.aiCategoryOrder && graphInsights.aiCategoryOrder.length > 0
|
|
88
|
+
? graphInsights.aiCategoryOrder
|
|
89
|
+
: [...DEFAULT_AI_CATEGORY_ORDER];
|
|
90
|
+
const aiCategoryScores = (graphInsights?.aiCategoryScores) || {};
|
|
91
|
+
const aiCategoryTopByGraph = (graphInsights?.aiCategoryTopBusiness) || {};
|
|
83
92
|
const roleTargets: Record<string, number> | undefined = (() => {
|
|
84
93
|
try {
|
|
85
94
|
const em: EnterpriseMatch | undefined = assessmentResult?.enterprise_match;
|
|
@@ -258,7 +267,7 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless }: { badgeData: Pub
|
|
|
258
267
|
</Reveal>
|
|
259
268
|
|
|
260
269
|
{/* Right: Contributing Factors (hidden on small screens) */}
|
|
261
|
-
<Reveal headless={isHeadless} className="lg:col-span-4 w-full
|
|
270
|
+
<Reveal headless={isHeadless} className="lg:col-span-4 w-full hidden lg:flex flex-col items-start justify-start" delayMs={80}>
|
|
262
271
|
<div className={'text-sm font-semibold mb-3'} style={{ color: 'var(--text-main)' }}>Top Contributing Factors</div>
|
|
263
272
|
<TopContributingFactors categories={genreMapping?.['Technical'] as string[] || []} categoryTopByGraph={categoryTopByGraph as any} />
|
|
264
273
|
</Reveal>
|
|
@@ -306,7 +315,7 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless }: { badgeData: Pub
|
|
|
306
315
|
/>
|
|
307
316
|
</Reveal>
|
|
308
317
|
{/* Right: Contributing Factors (hidden on small screens) */}
|
|
309
|
-
<Reveal headless={isHeadless} className="lg:col-span-4 w-full
|
|
318
|
+
<Reveal headless={isHeadless} className="lg:col-span-4 w-full hidden lg:flex flex-col items-start justify-start" delayMs={80}>
|
|
310
319
|
<div className={'text-sm font-semibold mb-3'} style={{ color: 'var(--text-main)' }}>Top Contributing Factors</div>
|
|
311
320
|
<TopContributingFactors categories={genreMapping?.['Risk'] as string[] || []} categoryTopByGraph={categoryTopByGraph as any} />
|
|
312
321
|
</Reveal>
|
|
@@ -343,6 +352,43 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless }: { badgeData: Pub
|
|
|
343
352
|
|
|
344
353
|
</div>
|
|
345
354
|
|
|
355
|
+
<div className="pt-8 space-y-8">
|
|
356
|
+
<Reveal headless={isHeadless} as={'h3'} offsetY={8} className={`${isHeadless ? 'kyd-break-before' : ''} text-2xl font-bold`} style={{ color: 'var(--text-main)' }}>KYD AI</Reveal>
|
|
357
|
+
<div className="grid grid-cols-1 lg:grid-cols-12 gap-8 w-full items-stretch py-8 border-y kyd-avoid-break" style={{ borderColor: 'var(--icon-button-secondary)' }}>
|
|
358
|
+
<Reveal headless={isHeadless} className="lg:col-span-8 h-full">
|
|
359
|
+
<CategoryBars
|
|
360
|
+
title={'AI Category Contributions'}
|
|
361
|
+
description={'Each bar represents AI evidence grouped into the major AI capability areas surfaced from GitHub business rules. Bar length reflects the strength of evidence for that AI domain.'}
|
|
362
|
+
categories={aiCategoryOrder as string[]}
|
|
363
|
+
categoryScores={aiCategoryScores}
|
|
364
|
+
getCategoryTooltipCopy={getCategoryTooltipCopy}
|
|
365
|
+
barHeight={16}
|
|
366
|
+
linkToAppendix={false}
|
|
367
|
+
tracksClassName={'min-h-[22rem] sm:min-h-[26rem] lg:min-h-[32rem]'}
|
|
368
|
+
/>
|
|
369
|
+
</Reveal>
|
|
370
|
+
<Reveal headless={isHeadless} className="lg:col-span-4 w-full hidden lg:flex flex-col items-start justify-start" delayMs={80}>
|
|
371
|
+
<div className={'text-sm font-semibold mb-3'} style={{ color: 'var(--text-main)' }}>Top Contributing Factors</div>
|
|
372
|
+
<TopContributingFactors categories={aiCategoryOrder as string[] || []} categoryTopByGraph={aiCategoryTopByGraph as any} />
|
|
373
|
+
</Reveal>
|
|
374
|
+
</div>
|
|
375
|
+
<Reveal headless={isHeadless}>
|
|
376
|
+
<div className="pt-8 border-t kyd-avoid-break" style={{ borderColor: 'var(--icon-button-secondary)' }}>
|
|
377
|
+
<h3 className={'text-xl font-bold mb-3 kyd-keep-with-next'} style={{ color: 'var(--text-main)' }}>KYD AI - Skills Insights</h3>
|
|
378
|
+
<div className={'prose prose-sm max-w-none mb-6 space-y-4'} style={{ color: 'var(--text-secondary)' }}>
|
|
379
|
+
<SkillsValidation
|
|
380
|
+
skillsCategoryRadar={graphInsights?.aiSkillsCategoryRadar}
|
|
381
|
+
headless={isHeadless}
|
|
382
|
+
title={'AI Skills by Subcategory'}
|
|
383
|
+
description={'The bar chart shows AI evidence strength by subcategory.'}
|
|
384
|
+
marginLeft={60}
|
|
385
|
+
marginRight={60}
|
|
386
|
+
/>
|
|
387
|
+
</div>
|
|
388
|
+
</div>
|
|
389
|
+
</Reveal>
|
|
390
|
+
</div>
|
|
391
|
+
|
|
346
392
|
{/* Connected Platforms */}
|
|
347
393
|
<Reveal headless={isHeadless}>
|
|
348
394
|
<ConnectedPlatforms accounts={connected} authenticity={assessmentResult?.account_authenticity} />
|
|
@@ -9,7 +9,7 @@ import EnterpriseCoaching from './components/EnterpriseCoaching';
|
|
|
9
9
|
import GraphInsights from './components/GraphInsights';
|
|
10
10
|
import IpRiskAnalysisDisplay from './components/IpRiskAnalysisDisplay';
|
|
11
11
|
import ReportHeader from './components/ReportHeader';
|
|
12
|
-
|
|
12
|
+
import AiIcon from './components/icons/ai';
|
|
13
13
|
import CodeIcon from './components/icons/code';
|
|
14
14
|
import RiskIcon from './components/icons/risk';
|
|
15
15
|
|
|
@@ -23,7 +23,6 @@ import ResumeView from './components/ResumeView';
|
|
|
23
23
|
import Reveal from './components/Reveal';
|
|
24
24
|
import RiskCard from './components/RiskCard';
|
|
25
25
|
import RoleOverviewCard from './components/RoleOverviewCard';
|
|
26
|
-
import SanctionsMatches from './components/SanctionsMatches';
|
|
27
26
|
import SkillsAppendixTable from './components/SkillsAppendixTable';
|
|
28
27
|
import SkillsBubble from './components/SkillsBubble';
|
|
29
28
|
import SkillsValidation from './components/SkillsValidation';
|
|
@@ -32,6 +31,15 @@ import UseCases from './components/UseCases';
|
|
|
32
31
|
import { formatLocalDateTime } from './utils/date';
|
|
33
32
|
import { getCategoryTooltipCopy } from './utils/provider';
|
|
34
33
|
|
|
34
|
+
const DEFAULT_AI_CATEGORY_ORDER = [
|
|
35
|
+
'Ecosystems & Providers',
|
|
36
|
+
'Orchestration & Dev',
|
|
37
|
+
'Specialized AI',
|
|
38
|
+
'Machine Learning',
|
|
39
|
+
'Infrastructure',
|
|
40
|
+
'Governance',
|
|
41
|
+
] as const;
|
|
42
|
+
|
|
35
43
|
type ChatWidgetProps = Partial<{
|
|
36
44
|
api: string;
|
|
37
45
|
title: string;
|
|
@@ -79,6 +87,12 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless, selfCheck = false,
|
|
|
79
87
|
const genreMapping = (scoringSummary?.config?.genre_mapping) || {};
|
|
80
88
|
const categoryScores = (scoringSummary?.category_scores) || {};
|
|
81
89
|
const categoryTopByGraph = (graphInsights?.categoryTopBusiness) || {};
|
|
90
|
+
const aiCategoryOrder =
|
|
91
|
+
graphInsights?.aiCategoryOrder && graphInsights.aiCategoryOrder.length > 0
|
|
92
|
+
? graphInsights.aiCategoryOrder
|
|
93
|
+
: [...DEFAULT_AI_CATEGORY_ORDER];
|
|
94
|
+
const aiCategoryScores = (graphInsights?.aiCategoryScores) || {};
|
|
95
|
+
const aiCategoryTopByGraph = (graphInsights?.aiCategoryTopBusiness) || {};
|
|
82
96
|
const roleTargets: Record<string, number> | undefined = (() => {
|
|
83
97
|
try {
|
|
84
98
|
const em: EnterpriseMatch | undefined = assessmentResult?.enterprise_match;
|
|
@@ -116,7 +130,7 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless, selfCheck = false,
|
|
|
116
130
|
{ key: 'overview', label: 'Overview' },
|
|
117
131
|
{ key: 'technical', label: 'KYD Technical' },
|
|
118
132
|
{ key: 'risk', label: 'KYD Risk' },
|
|
119
|
-
|
|
133
|
+
{ key: 'ai', label: 'KYD AI' },
|
|
120
134
|
{ key: 'role', label: showRoleFit ? 'Role Fit & Coaching' : 'Coaching' },
|
|
121
135
|
{ key: 'resume', label: 'KYD Resume' },
|
|
122
136
|
{ key: 'appendix', label: 'Appendix' }
|
|
@@ -165,7 +179,7 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless, selfCheck = false,
|
|
|
165
179
|
{tabs.map(t => {
|
|
166
180
|
// Map tab key to icon component
|
|
167
181
|
const IconComp = (() => {
|
|
168
|
-
|
|
182
|
+
if (t.key === 'ai') return AiIcon;
|
|
169
183
|
if (t.key === 'technical') return CodeIcon;
|
|
170
184
|
if (t.key === 'risk') return RiskIcon;
|
|
171
185
|
return null;
|
|
@@ -315,6 +329,20 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless, selfCheck = false,
|
|
|
315
329
|
</div>
|
|
316
330
|
</Reveal>
|
|
317
331
|
|
|
332
|
+
{/* AI Skills bar chart above Skills Footprint */}
|
|
333
|
+
<Reveal headless={isHeadless}>
|
|
334
|
+
<div className={'mt-6'}>
|
|
335
|
+
<SkillsValidation
|
|
336
|
+
skillsCategoryRadar={graphInsights?.aiSkillsCategoryRadar}
|
|
337
|
+
headless={isHeadless}
|
|
338
|
+
title={'AI Skills by Subcategory'}
|
|
339
|
+
description={'The bar chart shows AI evidence strength by subcategory.'}
|
|
340
|
+
marginLeft={60}
|
|
341
|
+
marginRight={60}
|
|
342
|
+
/>
|
|
343
|
+
</div>
|
|
344
|
+
</Reveal>
|
|
345
|
+
|
|
318
346
|
{/* Full-width Skills bubble chart below quadrant */}
|
|
319
347
|
<div className={'rounded-xl shadow-xl p-6 sm:p-8 mt-6 border'} style={{ backgroundColor: 'var(--content-card-background)', borderColor: 'var(--icon-button-secondary)' }}>
|
|
320
348
|
<Reveal headless={isHeadless}>
|
|
@@ -335,6 +363,10 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless, selfCheck = false,
|
|
|
335
363
|
// Local state for expanding long role descriptions
|
|
336
364
|
const [showFullRoleDescription, setShowFullRoleDescription] = useState(false);
|
|
337
365
|
const rec = assessmentResult?.recommendations;
|
|
366
|
+
const recommendationRoleName = (assessmentResult?.enterprise_match?.role?.name || '').trim();
|
|
367
|
+
const recommendationsTitle = recommendationRoleName
|
|
368
|
+
? `Evidence Recommendations for ${recommendationRoleName}`
|
|
369
|
+
: 'Evidence Recommendations';
|
|
338
370
|
|
|
339
371
|
return (
|
|
340
372
|
<div className={`${wrapperMaxWidth} mx-auto`}>
|
|
@@ -419,14 +451,14 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless, selfCheck = false,
|
|
|
419
451
|
</div>
|
|
420
452
|
)}
|
|
421
453
|
<div>
|
|
422
|
-
<Reveal headless={isHeadless} as={'h4'} offsetY={8} className={'text-2xl font-semibold mb-3'} style={{ color: 'var(--text-main)' }}>
|
|
454
|
+
<Reveal headless={isHeadless} as={'h4'} offsetY={8} className={'text-2xl font-semibold mb-3'} style={{ color: 'var(--text-main)' }}>{recommendationsTitle}</Reveal>
|
|
423
455
|
<Reveal headless={isHeadless}>
|
|
424
456
|
<div className={'space-y-3'}>
|
|
425
457
|
{rec?.summary ? (
|
|
426
458
|
<div className={'text-sm'} style={{ color: 'var(--text-secondary)' }}>{rec.summary}</div>
|
|
427
459
|
) : null}
|
|
428
460
|
{Array.isArray(rec?.bullet_points) && (rec?.bullet_points?.length || 0) > 0 ? (
|
|
429
|
-
<ul className={'list-disc pl-5 text-sm'} style={{ color: 'var(--text-secondary)' }}>
|
|
461
|
+
<ul className={'list-disc pl-5 space-y-3 text-sm'} style={{ color: 'var(--text-secondary)' }}>
|
|
430
462
|
{rec?.bullet_points?.map((bp, idx) => (
|
|
431
463
|
<li key={idx}>{bp}</li>
|
|
432
464
|
))}
|
|
@@ -489,7 +521,7 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless, selfCheck = false,
|
|
|
489
521
|
categoryTargets={roleTargets}
|
|
490
522
|
/>
|
|
491
523
|
</Reveal>
|
|
492
|
-
<Reveal headless={isHeadless} className="lg:col-span-4 w-full
|
|
524
|
+
<Reveal headless={isHeadless} className="lg:col-span-4 w-full hidden lg:flex flex-col items-start justify-start" delayMs={80}>
|
|
493
525
|
<div className={'text-sm font-semibold mb-3'} style={{ color: 'var(--text-main)' }}>Top Contributing Factors</div>
|
|
494
526
|
<TopContributingFactors categories={genreMapping?.['Technical'] as string[] || []} categoryTopByGraph={categoryTopByGraph as any} />
|
|
495
527
|
</Reveal>
|
|
@@ -506,6 +538,48 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless, selfCheck = false,
|
|
|
506
538
|
</div>
|
|
507
539
|
);
|
|
508
540
|
|
|
541
|
+
const AiSection = () => (
|
|
542
|
+
<div className={`${wrapperMaxWidth} mx-auto`}>
|
|
543
|
+
<div className={'rounded-xl shadow-xl p-6 sm:p-8 mt-6 border'} style={{ backgroundColor: 'var(--content-card-background)', borderColor: 'var(--icon-button-secondary)' }}>
|
|
544
|
+
<Reveal headless={isHeadless} as={'h4'} offsetY={8} className={'text-2xl font-semibold mb-3'} style={{ color: 'var(--text-main)' }}>KYD AI</Reveal>
|
|
545
|
+
<div className="grid grid-cols-1 lg:grid-cols-12 w-full gap-8 items-stretch py-8">
|
|
546
|
+
<Reveal headless={isHeadless} className="lg:col-span-8 h-full">
|
|
547
|
+
<CategoryBars
|
|
548
|
+
title={'AI Category Contributions'}
|
|
549
|
+
description={'Each bar represents AI evidence grouped into the major AI capability areas surfaced from GitHub business rules. Bar length reflects the strength of evidence for that AI domain.'}
|
|
550
|
+
categories={aiCategoryOrder as string[]}
|
|
551
|
+
categoryScores={aiCategoryScores}
|
|
552
|
+
getCategoryTooltipCopy={getCategoryTooltipCopy}
|
|
553
|
+
barHeight={16}
|
|
554
|
+
linkToAppendix={false}
|
|
555
|
+
tracksClassName={'min-h-[22rem] sm:min-h-[26rem] lg:min-h-[32rem]'}
|
|
556
|
+
/>
|
|
557
|
+
</Reveal>
|
|
558
|
+
<Reveal headless={isHeadless} className="lg:col-span-4 w-full hidden lg:flex flex-col items-start justify-start" delayMs={80}>
|
|
559
|
+
<div className={'text-sm font-semibold mb-3'} style={{ color: 'var(--text-main)' }}>Top Contributing Factors</div>
|
|
560
|
+
<TopContributingFactors categories={aiCategoryOrder as string[] || []} categoryTopByGraph={aiCategoryTopByGraph as any} />
|
|
561
|
+
</Reveal>
|
|
562
|
+
</div>
|
|
563
|
+
<Reveal headless={isHeadless}>
|
|
564
|
+
<div className="pt-8 border-t kyd-avoid-break" style={{ borderColor: 'var(--icon-button-secondary)' }}>
|
|
565
|
+
<h3 className={'text-xl font-bold mb-3 kyd-keep-with-next'} style={{ color: 'var(--text-main)' }}>KYD AI - Skills Insights</h3>
|
|
566
|
+
<div className={'prose prose-sm max-w-none mb-6 space-y-4'} style={{ color: 'var(--text-secondary)' }}>
|
|
567
|
+
<SkillsValidation
|
|
568
|
+
skillsCategoryRadar={graphInsights?.aiSkillsCategoryRadar}
|
|
569
|
+
headless={isHeadless}
|
|
570
|
+
title={'AI Skills by Subcategory'}
|
|
571
|
+
description={'The bar chart shows AI evidence strength by subcategory'}
|
|
572
|
+
marginLeft={60}
|
|
573
|
+
marginRight={60}
|
|
574
|
+
tickFontSize={12}
|
|
575
|
+
/>
|
|
576
|
+
</div>
|
|
577
|
+
</div>
|
|
578
|
+
</Reveal>
|
|
579
|
+
</div>
|
|
580
|
+
</div>
|
|
581
|
+
);
|
|
582
|
+
|
|
509
583
|
const RiskSection = () => {
|
|
510
584
|
const adjustedCategoryScores = (() => {
|
|
511
585
|
if (!badgeData.optOutScreening) return categoryScores;
|
|
@@ -557,7 +631,7 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless, selfCheck = false,
|
|
|
557
631
|
categoryTargets={roleTargets}
|
|
558
632
|
/>
|
|
559
633
|
</Reveal>
|
|
560
|
-
<Reveal headless={isHeadless} className="lg:col-span-4 w-full
|
|
634
|
+
<Reveal headless={isHeadless} className="lg:col-span-4 w-full hidden lg:flex flex-col items-start justify-start" delayMs={80}>
|
|
561
635
|
<div className={'text-sm font-semibold mb-3'} style={{ color: 'var(--text-main)' }}>Top Contributing Factors</div>
|
|
562
636
|
<TopContributingFactors categories={genreMapping?.['Risk'] as string[] || []} categoryTopByGraph={categoryTopByGraph as any} />
|
|
563
637
|
</Reveal>
|
|
@@ -594,15 +668,6 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless, selfCheck = false,
|
|
|
594
668
|
);
|
|
595
669
|
};
|
|
596
670
|
|
|
597
|
-
// const AiSection = () => (
|
|
598
|
-
// <div className={`${wrapperMaxWidth} mx-auto`}>
|
|
599
|
-
// <div className={'rounded-xl shadow-xl p-6 sm:p-8 mt-6 border'} style={{ backgroundColor: 'var(--content-card-background)', borderColor: 'var(--icon-button-secondary)' }}>
|
|
600
|
-
// <Reveal headless={isHeadless} as={'h4'} offsetY={8} className={'text-2xl font-semibold mb-3'} style={{ color: 'var(--text-main)' }}>KYD AI (Beta)</Reveal>
|
|
601
|
-
// <AiUsageBody ai={assessmentResult?.ai_usage_summary} />
|
|
602
|
-
// </div>
|
|
603
|
-
// </div>
|
|
604
|
-
// );
|
|
605
|
-
|
|
606
671
|
const AppendixSection = () => (
|
|
607
672
|
<div className={`${wrapperMaxWidth} mx-auto`}>
|
|
608
673
|
<div className={'rounded-xl shadow-xl p-6 sm:p-8 mt-6 border'} style={{ backgroundColor: 'var(--content-card-background)', borderColor: 'var(--icon-button-secondary)' }}>
|
|
@@ -697,7 +762,7 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless, selfCheck = false,
|
|
|
697
762
|
<RoleFitSection />
|
|
698
763
|
{TechnicalSection()}
|
|
699
764
|
{RiskSection()}
|
|
700
|
-
{
|
|
765
|
+
{AiSection()}
|
|
701
766
|
{AppendixSection()}
|
|
702
767
|
</>
|
|
703
768
|
) : (
|
|
@@ -709,7 +774,7 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless, selfCheck = false,
|
|
|
709
774
|
{activeTab === 'overview' && OverviewSection()}
|
|
710
775
|
{activeTab === 'technical' && TechnicalSection()}
|
|
711
776
|
{activeTab === 'risk' && RiskSection()}
|
|
712
|
-
{
|
|
777
|
+
{activeTab === 'ai' && AiSection()}
|
|
713
778
|
{activeTab === 'role' && <RoleFitSection />}
|
|
714
779
|
{activeTab === 'resume' && <ResumeSection />}
|
|
715
780
|
{activeTab === 'appendix' && AppendixSection()}
|
|
@@ -9,8 +9,12 @@ type CategoryBarsProps = {
|
|
|
9
9
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
10
10
|
categoryScores: Record<string, any>;
|
|
11
11
|
getCategoryTooltipCopy: (category: string) => string;
|
|
12
|
+
description?: string;
|
|
12
13
|
barHeight?: number; // px height for the filled bar
|
|
13
14
|
categoryTargets?: Record<string, number>; // desired target lengths (0-100)
|
|
15
|
+
linkToAppendix?: boolean;
|
|
16
|
+
/** Extra classes for the flex column that lays out rows with justify-between (taller column = more space between bars). */
|
|
17
|
+
tracksClassName?: string;
|
|
14
18
|
};
|
|
15
19
|
|
|
16
20
|
const CategoryBars: React.FC<CategoryBarsProps> = ({
|
|
@@ -18,21 +22,24 @@ const CategoryBars: React.FC<CategoryBarsProps> = ({
|
|
|
18
22
|
categories,
|
|
19
23
|
categoryScores,
|
|
20
24
|
getCategoryTooltipCopy,
|
|
25
|
+
description,
|
|
21
26
|
barHeight = 6,
|
|
22
27
|
categoryTargets,
|
|
28
|
+
linkToAppendix = true,
|
|
29
|
+
tracksClassName,
|
|
23
30
|
}) => {
|
|
24
31
|
return (
|
|
25
32
|
<div className="relative flex flex-col h-full">
|
|
26
33
|
<div className="font-semibold text-xl mb-2" style={{ color: 'var(--text-main)' }}>{title}</div>
|
|
27
34
|
<div className="text-sm mb-6" style={{ color: 'var(--text-secondary)' }}>
|
|
28
|
-
Each bar represents a category's net evidence: positive values extend right in green, negative values extend left, and bar length denotes contribution magnitude.
|
|
35
|
+
{description || "Each bar represents a category's net evidence: positive values extend right in green, negative values extend left, and bar length denotes contribution magnitude."}
|
|
29
36
|
{categoryTargets && Object.keys(categoryTargets).length > 0 ? (
|
|
30
37
|
<>
|
|
31
38
|
{' '}Target overlays indicate desired evidence lengths for this role.
|
|
32
39
|
</>
|
|
33
40
|
) : null}
|
|
34
41
|
</div>
|
|
35
|
-
<div className=
|
|
42
|
+
<div className={`flex-1 flex flex-col justify-between relative${tracksClassName ? ` ${tracksClassName}` : ''}`}>
|
|
36
43
|
<div
|
|
37
44
|
className="absolute top-0 bottom-0 w-px"
|
|
38
45
|
style={{
|
|
@@ -76,6 +83,7 @@ const CategoryBars: React.FC<CategoryBarsProps> = ({
|
|
|
76
83
|
const targetClamp = hasTarget ? Math.max(0, Math.min(100, Math.round(targetRaw as number))) : 0;
|
|
77
84
|
const targetWidth = targetClamp / 2; // same scale as fillWidth
|
|
78
85
|
const handleClick = (e: React.MouseEvent) => {
|
|
86
|
+
if (!linkToAppendix) return;
|
|
79
87
|
// Navigate to Appendix -> Skills category anchor
|
|
80
88
|
try {
|
|
81
89
|
if (typeof window !== 'undefined') {
|
|
@@ -87,7 +95,7 @@ const CategoryBars: React.FC<CategoryBarsProps> = ({
|
|
|
87
95
|
};
|
|
88
96
|
return (
|
|
89
97
|
<div key={category} className="first:pt-0 group relative">
|
|
90
|
-
<button type="button" onClick={handleClick} className={
|
|
98
|
+
<button type="button" onClick={handleClick} className={`font-semibold mb-1 underline-offset-2 text-left ${linkToAppendix ? 'hover:underline' : ''}`} style={{ color: 'var(--text-main)' }}>
|
|
91
99
|
{category}
|
|
92
100
|
</button>
|
|
93
101
|
<div className="relative">
|
|
@@ -99,8 +107,8 @@ const CategoryBars: React.FC<CategoryBarsProps> = ({
|
|
|
99
107
|
outline: '1px solid var(--icon-button-secondary)',
|
|
100
108
|
}}
|
|
101
109
|
onClick={handleClick}
|
|
102
|
-
role={'button'}
|
|
103
|
-
aria-label={`Jump to ${category} in Appendix`}
|
|
110
|
+
role={linkToAppendix ? 'button' : undefined}
|
|
111
|
+
aria-label={linkToAppendix ? `Jump to ${category} in Appendix` : undefined}
|
|
104
112
|
>
|
|
105
113
|
{/* signed fill originating from center, or full grey fill when no data */}
|
|
106
114
|
{!hasData ? (
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import { ResponsiveContainer, BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip } from 'recharts';
|
|
3
|
+
import { Bar, BarChart, CartesianGrid, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts';
|
|
5
4
|
import { green1, green2, green5 } from '../colors';
|
|
6
5
|
|
|
7
6
|
type SkillsRadarPoint = {
|
|
@@ -11,15 +10,31 @@ type SkillsRadarPoint = {
|
|
|
11
10
|
certified?: number;
|
|
12
11
|
};
|
|
13
12
|
|
|
14
|
-
export default function SkillsValidation({
|
|
15
|
-
|
|
13
|
+
export default function SkillsValidation({
|
|
14
|
+
skillsCategoryRadar,
|
|
15
|
+
headless,
|
|
16
|
+
title = 'Skills by Validation Type',
|
|
17
|
+
description = 'The bar chart shows how each skill is supported by self-attested claims, observed practice, or certified evidence.',
|
|
18
|
+
tickFontSize = 12,
|
|
19
|
+
marginLeft = 8,
|
|
20
|
+
marginRight = 8,
|
|
21
|
+
}: {
|
|
22
|
+
skillsCategoryRadar?: SkillsRadarPoint[];
|
|
23
|
+
headless?: boolean;
|
|
24
|
+
title?: string;
|
|
25
|
+
description?: string;
|
|
26
|
+
tickFontSize?: number;
|
|
27
|
+
marginLeft?: number;
|
|
28
|
+
marginRight?: number;
|
|
29
|
+
}) {
|
|
30
|
+
const skillsRadarLimited = skillsCategoryRadar || [];
|
|
16
31
|
const hasNoData = skillsRadarLimited.length === 0 || skillsRadarLimited.every(p => !p.observed && !p.self_reported && !p.certified);
|
|
17
32
|
const xAxisBottomOffset = 50 + 36; // XAxis height + BarChart margin.bottom
|
|
18
33
|
|
|
19
34
|
return (
|
|
20
35
|
<div className={'rounded-lg p-4 border kyd-avoid-break'} style={{ backgroundColor: 'var(--content-card-background)', borderColor: 'var(--icon-button-secondary)', breakInside: 'avoid', pageBreakInside: 'avoid' as unknown as undefined }}>
|
|
21
|
-
<h4 className={'font-semibold mb-3'} style={{ color: 'var(--text-main)' }}>
|
|
22
|
-
<p className={'text-sm mb-4'} style={{ color: 'var(--text-secondary)' }}>
|
|
36
|
+
<h4 className={'font-semibold mb-3'} style={{ color: 'var(--text-main)' }}>{title}</h4>
|
|
37
|
+
<p className={'text-sm mb-4'} style={{ color: 'var(--text-secondary)' }}>{description}</p>
|
|
23
38
|
<div style={{ height: 360, position: 'relative' }}>
|
|
24
39
|
{hasNoData && (
|
|
25
40
|
<div
|
|
@@ -40,15 +55,16 @@ export default function SkillsValidation({ skillsCategoryRadar, headless }: { sk
|
|
|
40
55
|
</div>
|
|
41
56
|
)}
|
|
42
57
|
<ResponsiveContainer>
|
|
43
|
-
<BarChart data={skillsRadarLimited} margin={{ top: 8, right:
|
|
58
|
+
<BarChart data={skillsRadarLimited} margin={{ top: 8, right: marginRight, left: marginLeft, bottom: 8 }}>
|
|
44
59
|
<CartesianGrid strokeDasharray="3 3" stroke={'var(--icon-button-secondary)'} />
|
|
45
|
-
<XAxis dataKey="axis" tick={{ fill: 'var(--text-secondary)', fontSize:
|
|
60
|
+
<XAxis dataKey="axis" tick={{ fill: 'var(--text-secondary)', fontSize: tickFontSize }} interval={0} angle={-20} textAnchor="end" height={100}/>
|
|
46
61
|
<YAxis domain={[0, 100]} tick={{ fill: 'var(--text-secondary)' }} />
|
|
47
62
|
<Tooltip contentStyle={{ background: 'var(--content-card-background)', border: `1px solid var(--icon-button-secondary)`, color: 'var(--text-main)' }} />
|
|
48
63
|
<Bar dataKey="observed" name="Observed" fill={green2} isAnimationActive={!headless} stroke={'var(--icon-button-secondary)'} strokeWidth={1} />
|
|
49
64
|
<Bar dataKey="self_reported" name="Self-reported" fill={green5} isAnimationActive={!headless} stroke={'var(--icon-button-secondary)'} strokeWidth={1} />
|
|
50
65
|
<Bar dataKey="certified" name="Certified" fill={green1} isAnimationActive={!headless} stroke={'var(--icon-button-secondary)'} strokeWidth={1} />
|
|
51
66
|
</BarChart>
|
|
67
|
+
|
|
52
68
|
</ResponsiveContainer>
|
|
53
69
|
</div>
|
|
54
70
|
</div>
|
package/src/types.ts
CHANGED
|
@@ -207,14 +207,30 @@ export interface EnterpriseUseCases {
|
|
|
207
207
|
note?: string;
|
|
208
208
|
}
|
|
209
209
|
|
|
210
|
+
export type RecommendationPriority = 'High' | 'Medium' | 'Low';
|
|
211
|
+
|
|
212
|
+
export type RecommendationItem = {
|
|
213
|
+
title: string;
|
|
214
|
+
priority: RecommendationPriority;
|
|
215
|
+
source: string;
|
|
216
|
+
recommendation: string;
|
|
217
|
+
evidence: string;
|
|
218
|
+
why_this_matters: string;
|
|
219
|
+
what_to_do_next: string;
|
|
220
|
+
expected_outcome: string;
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
export type Recommendations = {
|
|
224
|
+
summary?: string;
|
|
225
|
+
bullet_points?: string[];
|
|
226
|
+
items?: RecommendationItem[];
|
|
227
|
+
};
|
|
228
|
+
|
|
210
229
|
|
|
211
230
|
export interface AssessmentResult {
|
|
212
231
|
final_percent: number;
|
|
213
232
|
report_summary?: string;
|
|
214
|
-
recommendations:
|
|
215
|
-
summary?: string;
|
|
216
|
-
bullet_points?: string[];
|
|
217
|
-
};
|
|
233
|
+
recommendations: Recommendations;
|
|
218
234
|
developer_trust_explanation?: string;
|
|
219
235
|
industry_considerations?: string;
|
|
220
236
|
|
|
@@ -364,6 +380,27 @@ export type CategoryTopBusinessItem = {
|
|
|
364
380
|
rule_label?: string;
|
|
365
381
|
provider?: string;
|
|
366
382
|
weight?: number;
|
|
383
|
+
uid?: string;
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
export type CategoryScoreValue = {
|
|
387
|
+
percent_progress?: number;
|
|
388
|
+
percent_progress_signed?: number;
|
|
389
|
+
applied?: number;
|
|
390
|
+
theoretical?: number;
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
export type AiCategoryScoreEntry = {
|
|
394
|
+
business?: CategoryScoreValue;
|
|
395
|
+
combined?: CategoryScoreValue;
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
export type AiSkillsCategoryRadarPoint = {
|
|
399
|
+
axis: string;
|
|
400
|
+
observed?: number;
|
|
401
|
+
self_reported?: number;
|
|
402
|
+
certified?: number;
|
|
403
|
+
matched_rules?: number;
|
|
367
404
|
};
|
|
368
405
|
|
|
369
406
|
export interface GraphInsightsPayload {
|
|
@@ -436,6 +473,14 @@ export interface GraphInsightsPayload {
|
|
|
436
473
|
top_movers?: Array<{ label?: string; uid?: string }>;
|
|
437
474
|
};
|
|
438
475
|
};
|
|
476
|
+
aiCategoryOrder?: string[];
|
|
477
|
+
aiCategoryScores?: Record<string, AiCategoryScoreEntry>;
|
|
478
|
+
aiCategoryTopBusiness?: { [category: string]: CategoryTopBusinessItem[] };
|
|
479
|
+
aiSkillsCategoryRadar?: AiSkillsCategoryRadarPoint[];
|
|
480
|
+
aiSummary?: {
|
|
481
|
+
percent?: number;
|
|
482
|
+
label?: string;
|
|
483
|
+
};
|
|
439
484
|
ai_usage_summary?: {
|
|
440
485
|
explanation: string;
|
|
441
486
|
key_findings: string[];
|
package/src/utils/provider.tsx
CHANGED
|
@@ -60,6 +60,12 @@ export const getProviderTooltipCopy = (provider?: string): string => {
|
|
|
60
60
|
|
|
61
61
|
export const getCategoryTooltipCopy = (category: string): string => {
|
|
62
62
|
const name = (category || '').toLowerCase();
|
|
63
|
+
if (/ecosystems?\s*&\s*providers|model provider ecosystems|cross-provider interaction/.test(name)) return 'Signals showing breadth across model vendors and whether the developer works across multiple provider ecosystems.';
|
|
64
|
+
if (/orchestration\s*&\s*dev|orchestration|interaction frameworks|model development frameworks/.test(name)) return 'Signals showing how the developer structures, chains, and develops AI systems and workflows.';
|
|
65
|
+
if (/specialized ai|vision|image|audio|speech|multimodal/.test(name)) return 'Signals showing hands-on work with specialized AI modalities such as vision, speech, audio, and multimodal systems.';
|
|
66
|
+
if (/machine learning|classical|applied ml|training|optimization|mto/.test(name)) return 'Signals showing practical machine-learning knowledge, experimentation, and model optimization work.';
|
|
67
|
+
if (/infrastructure|embedding|retrieval|vector/.test(name)) return 'Signals showing AI infrastructure work including embeddings, retrieval systems, and vector database usage.';
|
|
68
|
+
if (/governance|evaluation|experimentation|safety|control/.test(name)) return 'Signals showing evaluation rigor, safety controls, and governance practices around AI systems.';
|
|
63
69
|
if (/network|connection|collab|peer/.test(name)) return 'Signals from the developer\'s professional connections, collaborations, and peer recognition.';
|
|
64
70
|
if (/project|repo|portfolio|work/.test(name)) return 'Signals from a developer\'s visible projects, repositories, or published work that indicate breadth and quality of output.';
|
|
65
71
|
if (/skill|cert|assessment|endorse/.test(name)) return 'Signals tied to specific technical abilities, such as verified certifications, assessments, or endorsements.';
|