kyd-shared-badge 0.3.45 → 0.3.47
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
|
@@ -137,6 +137,7 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless }: { badgeData: Pub
|
|
|
137
137
|
})()}
|
|
138
138
|
countries={(assessmentResult?.screening_sources?.ip_risk_analysis?.raw_data?.countries) || []}
|
|
139
139
|
accountAuthenticity={assessmentResult?.account_authenticity}
|
|
140
|
+
companyName={badgeData.companyName}
|
|
140
141
|
/>
|
|
141
142
|
</Reveal>
|
|
142
143
|
{/* Coaching / Evidence under header when present */}
|
|
@@ -31,6 +31,7 @@ import AiUsageBody from './components/AiUsageBody';
|
|
|
31
31
|
import SanctionsMatches from './components/SanctionsMatches';
|
|
32
32
|
import AppendixContent from './components/AppendixContent';
|
|
33
33
|
import { ProviderIcon, getProviderDisplayName, getProviderTooltipCopy, getCategoryTooltipCopy, barColor } from './utils/provider';
|
|
34
|
+
import { matchLabelToColor } from './colors';
|
|
34
35
|
import { useEffect, useMemo, useState } from 'react';
|
|
35
36
|
type ChatWidgetProps = Partial<{
|
|
36
37
|
api: string;
|
|
@@ -114,7 +115,7 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless }: { badgeData: Pub
|
|
|
114
115
|
const tabs = useMemo(() => {
|
|
115
116
|
const arr = [
|
|
116
117
|
{ key: 'overview', label: 'Overview' },
|
|
117
|
-
|
|
118
|
+
{ key: 'role', label: 'Role Fit & Coaching' },
|
|
118
119
|
{ key: 'technical', label: 'KYD Technical' },
|
|
119
120
|
{ key: 'risk', label: 'KYD Risk' },
|
|
120
121
|
{ key: 'ai', label: 'KYD AI' },
|
|
@@ -132,7 +133,7 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless }: { badgeData: Pub
|
|
|
132
133
|
const keys = new Set(tabs.map(t => t.key));
|
|
133
134
|
if (hash && keys.has(hash)) setActiveTab(hash);
|
|
134
135
|
// If the hash targets a skills appendix anchor, switch to Appendix
|
|
135
|
-
if (hash && (hash.startsWith('appendix-skills') || hash.includes('appendix-skills-cat-')
|
|
136
|
+
if (hash && (hash.startsWith('appendix-skills') || hash.includes('appendix-skills-cat-'))) {
|
|
136
137
|
setActiveTab('appendix');
|
|
137
138
|
}
|
|
138
139
|
} catch {}
|
|
@@ -215,8 +216,12 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless }: { badgeData: Pub
|
|
|
215
216
|
</div>
|
|
216
217
|
);
|
|
217
218
|
|
|
218
|
-
const RoleFitSection = () =>
|
|
219
|
-
|
|
219
|
+
const RoleFitSection = () => {
|
|
220
|
+
// Local state for expanding long role descriptions
|
|
221
|
+
const [showFullRoleDescription, setShowFullRoleDescription] = useState(false);
|
|
222
|
+
const rec = assessmentResult?.recommendations;
|
|
223
|
+
|
|
224
|
+
return (
|
|
220
225
|
<div className={`${wrapperMaxWidth} mx-auto`}>
|
|
221
226
|
<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)' }}>
|
|
222
227
|
<div className="space-y-10">
|
|
@@ -224,19 +229,46 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless }: { badgeData: Pub
|
|
|
224
229
|
<div>
|
|
225
230
|
<Reveal headless={isHeadless} as={'h4'} offsetY={8} className={'text-2xl font-semibold mb-3'} style={{ color: 'var(--text-main)' }}>Role Alignment</Reveal>
|
|
226
231
|
<Reveal headless={isHeadless}>
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
232
|
+
{(() => {
|
|
233
|
+
const em = assessmentResult?.enterprise_match;
|
|
234
|
+
if (!em) return null;
|
|
235
|
+
const role = em.role || {};
|
|
236
|
+
const roleName = role?.name || '';
|
|
237
|
+
const primaryDescription = (role?.description || em.description || '').trim();
|
|
238
|
+
const isLong = primaryDescription.length > 500;
|
|
239
|
+
const visibleText = isLong && !showFullRoleDescription ? `${primaryDescription.slice(0, 500)}…` : primaryDescription;
|
|
240
|
+
return (
|
|
241
|
+
<div className={'text-sm'} style={{ color: 'var(--text-secondary)' }}>
|
|
242
|
+
<div className="flex items-center text-start gap-2 py-3">
|
|
243
|
+
<div className={'text-base font-semibold'} style={{ color: 'var(--text-main)' }}>{roleName}</div>
|
|
244
|
+
<span>-</span>
|
|
245
|
+
<div
|
|
246
|
+
className={
|
|
247
|
+
`px-3 py-1 inline-block rounded-md font-semibold shadow-sm text-[var(--text-main)] border`
|
|
248
|
+
}
|
|
249
|
+
style={{ borderColor: matchLabelToColor(em.label) }}
|
|
250
|
+
>
|
|
251
|
+
{em.label}
|
|
252
|
+
</div>
|
|
236
253
|
</div>
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
254
|
+
{primaryDescription ? (
|
|
255
|
+
<div>
|
|
256
|
+
<span>{visibleText}</span>
|
|
257
|
+
{isLong && (
|
|
258
|
+
<button
|
|
259
|
+
type="button"
|
|
260
|
+
onClick={() => setShowFullRoleDescription(v => !v)}
|
|
261
|
+
className={'ml-2 text-xs font-semibold'}
|
|
262
|
+
style={{ color: 'var(--text-main)' }}
|
|
263
|
+
>
|
|
264
|
+
{showFullRoleDescription ? 'See less' : 'See more'}
|
|
265
|
+
</button>
|
|
266
|
+
)}
|
|
267
|
+
</div>
|
|
268
|
+
) : null}
|
|
269
|
+
</div>
|
|
270
|
+
);
|
|
271
|
+
})()}
|
|
240
272
|
</Reveal>
|
|
241
273
|
</div>
|
|
242
274
|
)}
|
|
@@ -258,11 +290,30 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless }: { badgeData: Pub
|
|
|
258
290
|
<UseCases useCases={assessmentResult?.enterprise_use_cases} headless={isHeadless} badgeId={badgeId} />
|
|
259
291
|
</div>
|
|
260
292
|
)}
|
|
293
|
+
<div>
|
|
294
|
+
<Reveal headless={isHeadless} as={'h4'} offsetY={8} className={'text-2xl font-semibold mb-3'} style={{ color: 'var(--text-main)' }}>Recommendations</Reveal>
|
|
295
|
+
<Reveal headless={isHeadless}>
|
|
296
|
+
<div className={'space-y-3'}>
|
|
297
|
+
{rec?.summary ? (
|
|
298
|
+
<div className={'text-sm'} style={{ color: 'var(--text-secondary)' }}>{rec.summary}</div>
|
|
299
|
+
) : null}
|
|
300
|
+
{Array.isArray(rec?.bullet_points) && (rec?.bullet_points?.length || 0) > 0 ? (
|
|
301
|
+
<ul className={'list-disc pl-5 text-sm'} style={{ color: 'var(--text-secondary)' }}>
|
|
302
|
+
{rec?.bullet_points?.map((bp, idx) => (
|
|
303
|
+
<li key={idx}>{bp}</li>
|
|
304
|
+
))}
|
|
305
|
+
</ul>
|
|
306
|
+
) : (!rec?.summary ? (
|
|
307
|
+
<div className={'text-sm'} style={{ color: 'var(--text-secondary)' }}>No recommendations available.</div>
|
|
308
|
+
) : null)}
|
|
309
|
+
</div>
|
|
310
|
+
</Reveal>
|
|
311
|
+
</div>
|
|
261
312
|
</div>
|
|
262
313
|
</div>
|
|
263
314
|
</div>
|
|
264
|
-
)
|
|
265
|
-
|
|
315
|
+
);
|
|
316
|
+
};
|
|
266
317
|
|
|
267
318
|
const TechnicalSection = () => (
|
|
268
319
|
<div className={`${wrapperMaxWidth} mx-auto`}>
|
|
@@ -428,7 +479,7 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless }: { badgeData: Pub
|
|
|
428
479
|
})()}
|
|
429
480
|
</div>
|
|
430
481
|
)}
|
|
431
|
-
<div
|
|
482
|
+
<div>
|
|
432
483
|
<ConnectedPlatforms accounts={connected} authenticity={assessmentResult?.account_authenticity} />
|
|
433
484
|
</div>
|
|
434
485
|
</div>
|
|
@@ -461,7 +512,7 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless }: { badgeData: Pub
|
|
|
461
512
|
</style>
|
|
462
513
|
{/* Long-form original layout retained for headless/print */}
|
|
463
514
|
{OverviewSection()}
|
|
464
|
-
|
|
515
|
+
<RoleFitSection />
|
|
465
516
|
{TechnicalSection()}
|
|
466
517
|
{RiskSection()}
|
|
467
518
|
{AiSection()}
|
|
@@ -472,7 +523,7 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless }: { badgeData: Pub
|
|
|
472
523
|
<TabNav />
|
|
473
524
|
<div className={'px-2 sm:px-0 pb-2'}>
|
|
474
525
|
{activeTab === 'overview' && OverviewSection()}
|
|
475
|
-
{activeTab === 'role' && RoleFitSection
|
|
526
|
+
{activeTab === 'role' && <RoleFitSection />}
|
|
476
527
|
{activeTab === 'technical' && TechnicalSection()}
|
|
477
528
|
{activeTab === 'risk' && RiskSection()}
|
|
478
529
|
{activeTab === 'ai' && AiSection()}
|
package/src/colors.ts
CHANGED
|
@@ -96,5 +96,11 @@ export function clampPercent(value?: number): number {
|
|
|
96
96
|
return `rgba(${r}, ${g}, ${b}, ${alpha})`
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
export function matchLabelToColor(label: string): string {
|
|
100
|
+
const l = (label || '').toLowerCase();
|
|
101
|
+
if (l.includes('optimal') || l.includes('strong')) return green
|
|
102
|
+
if (l.includes('moderate')) return yellow
|
|
103
|
+
return red
|
|
104
|
+
}
|
|
99
105
|
|
|
100
106
|
|
|
@@ -62,12 +62,13 @@ const ConnectedPlatforms = ({ accounts, authenticity }: { accounts?: ConnectedAc
|
|
|
62
62
|
const label = (authenticity?.label || '').toLowerCase();
|
|
63
63
|
const isCritical = label === 'likely inauthentic';
|
|
64
64
|
const isSuspicious = label === 'suspicious';
|
|
65
|
+
const displayLabel = isSuspicious ? 'Warning' : label
|
|
65
66
|
if (!label) return null;
|
|
66
67
|
return (
|
|
67
68
|
<div className={"mb-4 rounded-md border p-3"} style={{ backgroundColor: 'var(--content-card-background)', borderColor: 'var(--icon-button-secondary)' }}>
|
|
68
69
|
<div className="flex items-start gap-2">
|
|
69
70
|
<span className="text-sm font-semibold" style={{ color: isCritical ? '#B00020' : isSuspicious ? '#9f580a' : 'var(--text-main)' }}>
|
|
70
|
-
{
|
|
71
|
+
{displayLabel}
|
|
71
72
|
</span>
|
|
72
73
|
</div>
|
|
73
74
|
{authenticity?.description ? (
|
|
@@ -40,9 +40,10 @@ interface ReportHeaderProps {
|
|
|
40
40
|
enterpriseMatch?: { label?: string; description?: string; roleName?: string } | null;
|
|
41
41
|
countries?: string[];
|
|
42
42
|
accountAuthenticity?: { label?: string; description?: string };
|
|
43
|
+
companyName?: string;
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
const ReportHeader = ({ badgeId, developerName, updatedAt, score = 0, badgeImageUrl, summary, enterpriseMatch, countries = [], accountAuthenticity }: ReportHeaderProps) => {
|
|
46
|
+
const ReportHeader = ({ badgeId, developerName, updatedAt, score = 0, badgeImageUrl, summary, enterpriseMatch, countries = [], accountAuthenticity, companyName }: ReportHeaderProps) => {
|
|
46
47
|
// Use the dynamic image if available, otherwise fall back to the score-based one.
|
|
47
48
|
const finalBadgeImageUrl = badgeImageUrl || getBadgeImageUrl(score || 0);
|
|
48
49
|
const tint = hexToRgba(pickTint(score || 0), 0.06);
|
|
@@ -102,7 +103,7 @@ const ReportHeader = ({ badgeId, developerName, updatedAt, score = 0, badgeImage
|
|
|
102
103
|
<div className={'text-sm space-y-2'}>
|
|
103
104
|
<p><span className={'font-semibold'} style={{ color: 'var(--text-secondary)' }}>Developer:</span> <span style={{ color: 'var(--text-main)' }}>{developerName || 'N/A'}</span></p>
|
|
104
105
|
<p><span className={'font-semibold'} style={{ color: 'var(--text-secondary)' }}>Requested By:</span> <span style={{ color: 'var(--text-main)' }}>{developerName || 'N/A'}</span></p>
|
|
105
|
-
<p><span className={'font-semibold'} style={{ color: 'var(--text-secondary)' }}>Organization:</span> <span style={{ color: 'var(--text-main)' }}>Unaffiliated</span></p>
|
|
106
|
+
<p><span className={'font-semibold'} style={{ color: 'var(--text-secondary)' }}>Organization:</span> <span style={{ color: 'var(--text-main)' }}>{companyName || 'Unaffiliated'}</span></p>
|
|
106
107
|
<p><span className={'font-semibold'} style={{ color: 'var(--text-secondary)' }}>Date Generated:</span> <span style={{ color: 'var(--text-main)' }}>{formattedDate}</span></p>
|
|
107
108
|
<p><span className={'font-semibold'} style={{ color: 'var(--text-secondary)' }}>Report ID:</span> <span style={{ color: 'var(--text-main)' }}>{badgeId || 'N/A'}</span></p>
|
|
108
109
|
{Array.isArray(countries) && countries.length > 0 && (
|
package/src/types.ts
CHANGED
|
@@ -34,6 +34,7 @@ export interface PublicBadgeData {
|
|
|
34
34
|
}[];
|
|
35
35
|
optOutScreening?: boolean;
|
|
36
36
|
isPublic?: boolean;
|
|
37
|
+
companyName?: string;
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
export type VerifiedBadge = {
|
|
@@ -172,7 +173,7 @@ export interface EnterpriseMatch {
|
|
|
172
173
|
description: string;
|
|
173
174
|
categoryTargets?: Record<string, number>;
|
|
174
175
|
};
|
|
175
|
-
label:
|
|
176
|
+
label: 'Incompatible' | 'Weak Match' | 'Moderate Match' | 'Strong Match' | 'Optimal Match';
|
|
176
177
|
description: string;
|
|
177
178
|
coaching?: EnterpriseCoaching;
|
|
178
179
|
}
|