kyd-shared-badge 0.3.128 → 0.3.130
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
|
@@ -211,6 +211,28 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless, selfCheck = false,
|
|
|
211
211
|
</div>
|
|
212
212
|
);
|
|
213
213
|
|
|
214
|
+
const AuthenticityNotice = () => {
|
|
215
|
+
const label = (assessmentResult?.account_authenticity?.label || '').toLowerCase();
|
|
216
|
+
if (!label) return null;
|
|
217
|
+
const isCritical = label === 'likely inauthentic';
|
|
218
|
+
const isSuspicious = label === 'suspicious';
|
|
219
|
+
if (!isCritical && !isSuspicious) return null;
|
|
220
|
+
return (
|
|
221
|
+
<div className={'mt-4'}>
|
|
222
|
+
<div className={'rounded-md border p-3 flex items-start gap-2'} style={{ backgroundColor: 'var(--content-card-background)', borderColor: 'var(--icon-button-secondary)' }}>
|
|
223
|
+
<span className={'mt-0.5'} style={{ color: isCritical ? '#B00020' : '#9f580a' }}><FiAlertTriangle size={18} /></span>
|
|
224
|
+
<div className={'text-sm'}>
|
|
225
|
+
<div className={'font-semibold'} style={{ color: 'var(--text-main)' }}>{isCritical ? 'Warning' : 'Attention'}</div>
|
|
226
|
+
<div className={'mt-1'} style={{ color: 'var(--text-secondary)' }}>{assessmentResult?.account_authenticity?.description || (isCritical ? 'We detected signals that some linked accounts may be inauthentic. Review sources below.' : 'Some inconsistencies were observed across linked accounts. Review sources below.')}</div>
|
|
227
|
+
<div className={'mt-2'}>
|
|
228
|
+
<a href="#appendix-connected" className={'text-xs font-medium underline underline-offset-2'} style={{ color: 'var(--text-secondary)' }}>See more</a>
|
|
229
|
+
</div>
|
|
230
|
+
</div>
|
|
231
|
+
</div>
|
|
232
|
+
</div>
|
|
233
|
+
);
|
|
234
|
+
};
|
|
235
|
+
|
|
214
236
|
const OverviewSection = () => (
|
|
215
237
|
<div className={`${wrapperMaxWidth} mx-auto mt-6`}>
|
|
216
238
|
<Reveal headless={isHeadless} offsetY={8} durationMs={500}>
|
|
@@ -387,7 +409,7 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless, selfCheck = false,
|
|
|
387
409
|
</div>
|
|
388
410
|
)}
|
|
389
411
|
<div>
|
|
390
|
-
<Reveal headless={isHeadless} as={'h4'} offsetY={8} className={'text-2xl font-semibold mb-3'} style={{ color: 'var(--text-main)' }}>Score Improvement
|
|
412
|
+
<Reveal headless={isHeadless} as={'h4'} offsetY={8} className={'text-2xl font-semibold mb-3'} style={{ color: 'var(--text-main)' }}>Score Improvement Recommendations</Reveal>
|
|
391
413
|
<Reveal headless={isHeadless}>
|
|
392
414
|
<div className={'space-y-3'}>
|
|
393
415
|
{rec?.summary ? (
|
|
@@ -631,6 +653,7 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless, selfCheck = false,
|
|
|
631
653
|
}`}
|
|
632
654
|
</style>
|
|
633
655
|
{/* Long-form original layout retained for headless/print */}
|
|
656
|
+
<AuthenticityNotice />
|
|
634
657
|
{OverviewSection()}
|
|
635
658
|
<RoleFitSection />
|
|
636
659
|
{TechnicalSection()}
|
|
@@ -643,6 +666,7 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless, selfCheck = false,
|
|
|
643
666
|
<>
|
|
644
667
|
<TabNav />
|
|
645
668
|
<div className={'px-2 sm:px-0 pb-2'}>
|
|
669
|
+
{activeTab === 'overview' && <AuthenticityNotice />}
|
|
646
670
|
{activeTab === 'overview' && OverviewSection()}
|
|
647
671
|
{activeTab === 'technical' && TechnicalSection()}
|
|
648
672
|
{activeTab === 'risk' && RiskSection()}
|
|
@@ -653,7 +677,10 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless, selfCheck = false,
|
|
|
653
677
|
</div>
|
|
654
678
|
</>
|
|
655
679
|
) : (
|
|
656
|
-
|
|
680
|
+
<>
|
|
681
|
+
<AuthenticityNotice />
|
|
682
|
+
{OverviewSection()}
|
|
683
|
+
</>
|
|
657
684
|
)
|
|
658
685
|
)}
|
|
659
686
|
{!headless && !suppressChat && (
|
|
@@ -9,6 +9,7 @@ import { red, yellow, green, hexToRgba } from '../colors';
|
|
|
9
9
|
import RiskIcon from './icons/risk';
|
|
10
10
|
import CodeIcon from './icons/code';
|
|
11
11
|
import AiIcon from './icons/ai';
|
|
12
|
+
import { ProviderIcon } from '../utils/provider';
|
|
12
13
|
|
|
13
14
|
interface SanctionSource {
|
|
14
15
|
issuingEntity: string;
|
|
@@ -304,6 +305,7 @@ const AppendixTables: React.FC<AppendixTableProps> = ({ type, sources, searchedA
|
|
|
304
305
|
<th
|
|
305
306
|
key={header}
|
|
306
307
|
scope="col"
|
|
308
|
+
title={header === 'Observation' ? 'Observation attribute details are part of the KYD proprietary framework and are available on a case-by-case basis.' : undefined}
|
|
307
309
|
onClick={() => onHeaderClick(header)}
|
|
308
310
|
className={'px-4 py-3 text-left text-xs font-semibold uppercase tracking-wider'}
|
|
309
311
|
style={{ color: 'var(--text-secondary)', cursor: isSortable ? 'pointer' : 'default', userSelect: isSortable ? 'none' : 'auto' }}
|
|
@@ -337,22 +339,6 @@ const AppendixTables: React.FC<AppendixTableProps> = ({ type, sources, searchedA
|
|
|
337
339
|
}
|
|
338
340
|
const br = source as BusinessRuleRow & { pillar?: string };
|
|
339
341
|
const anchorId = br.uid ? `rule-${br.uid}` : undefined;
|
|
340
|
-
const ProviderIcon = ({ name, pillar }: { name?: string, pillar?: string }) => {
|
|
341
|
-
const n = (name || '').toLowerCase();
|
|
342
|
-
if (n.includes('github')) return <FaGithub />;
|
|
343
|
-
if (n.includes('gitlab')) return <FaGitlab />;
|
|
344
|
-
if (n.includes('stack')) return <FaStackOverflow />;
|
|
345
|
-
if (n.includes('credly')) return <SiCredly />;
|
|
346
|
-
if (n.includes('fiverr')) return <SiFiverr />;
|
|
347
|
-
if (n.includes('kaggle')) return <FaKaggle />;
|
|
348
|
-
if (n.includes('google')) return <FaGoogle />;
|
|
349
|
-
if (n.includes('linkedin')) return <FaLinkedin />;
|
|
350
|
-
const p = (pillar || '').toLowerCase();
|
|
351
|
-
if (p === 'risk') return <RiskIcon width={28} height={28} />;
|
|
352
|
-
if (p === 'technical') return <CodeIcon width={28} height={28} />;
|
|
353
|
-
if (p === 'ai') return <AiIcon width={28} height={28} />;
|
|
354
|
-
return <span className="inline-block w-8 h-8 rounded-full" style={{ backgroundColor: 'var(--icon-button-secondary)' }} />;
|
|
355
|
-
};
|
|
356
342
|
return (
|
|
357
343
|
<tr
|
|
358
344
|
id={anchorId}
|
|
@@ -369,12 +355,18 @@ const AppendixTables: React.FC<AppendixTableProps> = ({ type, sources, searchedA
|
|
|
369
355
|
>
|
|
370
356
|
<td className={'px-4 py-4 whitespace-normal text-sm'} style={{ color: 'var(--text-secondary)' }}>
|
|
371
357
|
<span title={br.provider || ''} className={'inline-flex items-center text-4xl'} style={{ color: 'var(--text-main)' }}>
|
|
372
|
-
<ProviderIcon name={br.provider}
|
|
358
|
+
<ProviderIcon name={br.provider} className='relative w-8 h-8' />
|
|
373
359
|
</span>
|
|
374
360
|
</td>
|
|
375
361
|
<td className={'px-4 py-4 whitespace-normal text-sm'} style={{ color: 'var(--text-secondary)' }}>{br.pillar || '—'}</td>
|
|
376
362
|
<td className={'px-4 py-4 whitespace-normal text-sm'} style={{ color: 'var(--text-secondary)' }}>{br.category || '—'}</td>
|
|
377
|
-
<td
|
|
363
|
+
<td
|
|
364
|
+
className={'px-4 py-4 whitespace-normal text-sm font-medium'}
|
|
365
|
+
style={{ color: 'var(--text-main)' }}
|
|
366
|
+
title={'Observation attribute details are part of the KYD proprietary framework and are available on a case-by-case basis.'}
|
|
367
|
+
>
|
|
368
|
+
-
|
|
369
|
+
</td>
|
|
378
370
|
<td className={'px-4 py-4 whitespace-normal text-sm'} style={{ color: 'var(--text-secondary)' }}>{(() => {
|
|
379
371
|
const weight = Number(br.weight);
|
|
380
372
|
if (!Number.isFinite(weight)) return 'Neutral';
|
|
@@ -4,7 +4,7 @@ import Image from 'next/image';
|
|
|
4
4
|
import { formatLocalDate } from '../utils/date';
|
|
5
5
|
import countriesLib from 'i18n-iso-countries';
|
|
6
6
|
import enLocale from 'i18n-iso-countries/langs/en.json';
|
|
7
|
-
import { FiInfo
|
|
7
|
+
import { FiInfo } from 'react-icons/fi';
|
|
8
8
|
import { ProviderIcon } from '../utils/provider';
|
|
9
9
|
import { green1, green2, green3, green4, green5 } from '../colors';
|
|
10
10
|
|
|
@@ -80,25 +80,7 @@ const ReportHeader = ({ badgeId, developerName, updatedAt, score = 0, badgeImage
|
|
|
80
80
|
className={'p-6 rounded-md border h-full'}
|
|
81
81
|
style={{ backgroundColor: 'var(--content-card-background)', borderColor: 'var(--icon-button-secondary)', backgroundImage: `linear-gradient(${tint}, ${tint})` }}
|
|
82
82
|
>
|
|
83
|
-
{
|
|
84
|
-
const label = (accountAuthenticity?.label || '').toLowerCase();
|
|
85
|
-
if (!label) return null;
|
|
86
|
-
const isCritical = label === 'likely inauthentic';
|
|
87
|
-
const isSuspicious = label === 'suspicious';
|
|
88
|
-
if (!isCritical && !isSuspicious) return null;
|
|
89
|
-
return (
|
|
90
|
-
<div className={'mb-4 rounded-md border p-3 flex items-start gap-2'} style={{ backgroundColor: 'var(--content-card-background)', borderColor: 'var(--icon-button-secondary)' }}>
|
|
91
|
-
<span className={'mt-0.5'} style={{ color: isCritical ? '#B00020' : '#9f580a' }}><FiAlertTriangle size={18} /></span>
|
|
92
|
-
<div className={'text-sm'}>
|
|
93
|
-
<div className={'font-semibold'} style={{ color: 'var(--text-main)' }}>{isCritical ? 'Warning' : 'Attention'}</div>
|
|
94
|
-
<div className={'mt-1'} style={{ color: 'var(--text-secondary)' }}>{accountAuthenticity?.description || (isCritical ? 'We detected signals that some linked accounts may be inauthentic. Review sources below.' : 'Some inconsistencies were observed across linked accounts. Review sources below.')}</div>
|
|
95
|
-
<div className={'mt-2'}>
|
|
96
|
-
<a href="#appendix-connected" className={'text-xs font-medium underline underline-offset-2'} style={{ color: 'var(--text-secondary)' }}>See more</a>
|
|
97
|
-
</div>
|
|
98
|
-
</div>
|
|
99
|
-
</div>
|
|
100
|
-
);
|
|
101
|
-
})()}
|
|
83
|
+
{/* Authenticity notice moved to top-level display */}
|
|
102
84
|
<div className={`flex gap-3 ${rightBadgeLayout ? 'flex-row items-center justify-between' : 'flex-col h-full'}`}>
|
|
103
85
|
{/* Info section: Title, Candidate, Details and Summary */}
|
|
104
86
|
<div className={`w-full ${rightBadgeLayout ? 'md:flex-1' : ''}`}>
|
|
@@ -172,7 +172,7 @@ export default function SkillsBubble({ skillsCategoryRadar, skillsByCategory, sk
|
|
|
172
172
|
if (list.length > 10) {
|
|
173
173
|
// Aggregate years and sources for remaining datapoints beyond the top 10
|
|
174
174
|
const remaining = enriched.slice(10);
|
|
175
|
-
const aggregatedYears = remaining.reduce((sum, it) => sum + Number(it.years || 0), 0);
|
|
175
|
+
const aggregatedYears = Math.round(remaining.reduce((sum, it) => sum + Number(it.years || 0), 0) * 10) / 10;
|
|
176
176
|
const aggregatedSourcesSet = new Set<string>();
|
|
177
177
|
for (const it of remaining) {
|
|
178
178
|
if (Array.isArray(it.sources)) {
|
package/src/utils/provider.tsx
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { FaGithub, FaGitlab, FaStackOverflow, FaLinkedin, FaGoogle, FaKaggle } from 'react-icons/fa';
|
|
4
|
-
import { SiCoursera, SiCredly, SiFiverr,
|
|
3
|
+
import { FaGithub, FaGitlab, FaStackOverflow, FaLinkedin, FaGoogle, FaKaggle, FaShieldAlt } from 'react-icons/fa';
|
|
4
|
+
import { SiCoursera, SiCredly, SiFiverr, SiUdemy, SiAmazon } from 'react-icons/si';
|
|
5
|
+
import RiskIcon from '../components/icons/risk';
|
|
6
|
+
import CodeIcon from '../components/icons/code';
|
|
7
|
+
import AiIcon from '../components/icons/ai';
|
|
5
8
|
|
|
6
9
|
export const ProviderIcon = ({ name, className }: { name?: string; className?: string }) => {
|
|
7
10
|
const n = (name || '').toLowerCase();
|
|
@@ -11,11 +14,15 @@ export const ProviderIcon = ({ name, className }: { name?: string; className?: s
|
|
|
11
14
|
if (n.includes('credly')) return <SiCredly className={className} />;
|
|
12
15
|
if (n.includes('fiverr')) return <SiFiverr className={className} />;
|
|
13
16
|
if (n.includes('kaggle')) return <FaKaggle className={className} />;
|
|
14
|
-
if (n.includes('google')) return <FaGoogle className={className} />;
|
|
17
|
+
if (n.includes('google') || n.includes('gscholar')) return <FaGoogle className={className} />;
|
|
15
18
|
if (n.includes('linkedin')) return <FaLinkedin className={className} />;
|
|
16
|
-
if (n.includes('toptal')) return <SiToptal className={className} />;
|
|
17
19
|
if (n.includes('coursera')) return <SiCoursera className={className} />;
|
|
18
20
|
if (n.includes('udemy')) return <SiUdemy className={className} />;
|
|
21
|
+
if (n.includes('amazon')) return <SiAmazon className={className} />;
|
|
22
|
+
if (n.includes('cyber')) return <RiskIcon className={className} />;
|
|
23
|
+
if (n.includes('risk')) return <RiskIcon className={className} />;
|
|
24
|
+
if (n.includes('technical')) return <CodeIcon className={className} />;
|
|
25
|
+
if (n.includes('ai')) return <AiIcon className={className} />;
|
|
19
26
|
return <span className={className || 'inline-block w-3 h-3 rounded-full'} style={{ backgroundColor: 'var(--icon-button-secondary)' }} />;
|
|
20
27
|
};
|
|
21
28
|
|
|
@@ -29,9 +36,9 @@ export const getProviderDisplayName = (name?: string): string => {
|
|
|
29
36
|
if (n.includes('kaggle')) return 'Kaggle';
|
|
30
37
|
if (n.includes('google')) return 'Google Scholar';
|
|
31
38
|
if (n.includes('linkedin')) return 'LinkedIn';
|
|
32
|
-
if (n.includes('toptal')) return 'TopTal';
|
|
33
39
|
if (n.includes('coursera')) return 'Coursera';
|
|
34
40
|
if (n.includes('udemy')) return 'Udemy';
|
|
41
|
+
if (n.includes('amazon')) return 'Amazon Author';
|
|
35
42
|
return name || 'Provider';
|
|
36
43
|
};
|
|
37
44
|
|
|
@@ -45,9 +52,9 @@ export const getProviderTooltipCopy = (provider?: string): string => {
|
|
|
45
52
|
if (n.includes('kaggle')) return 'Competition results, notebooks, and dataset contributions that reflect analytical skill.';
|
|
46
53
|
if (n.includes('google')) return 'Publications, citations, and scholarly presence indicating research impact.';
|
|
47
54
|
if (n.includes('linkedin')) return 'Professional history, endorsements, and network signals indicating credibility.';
|
|
48
|
-
if (n.includes('toptal')) return 'Professional history, endorsements, and network signals indicating credibility.';
|
|
49
55
|
if (n.includes('coursera')) return 'Professional history, training, and course completion signals indicating credibility.';
|
|
50
56
|
if (n.includes('udemy')) return 'Professional history, training, and course completion signals indicating credibility.';
|
|
57
|
+
if (n.includes('amazon')) return 'Published works and author profile signals from Amazon Stores indicating credibility.';
|
|
51
58
|
return 'Signals contributed from this provider relevant to capability and trust.';
|
|
52
59
|
};
|
|
53
60
|
|