kyd-shared-badge 0.3.141 → 0.3.200
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
|
@@ -139,7 +139,8 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless }: { badgeData: Pub
|
|
|
139
139
|
countries={(assessmentResult?.screening_sources?.ip_risk_analysis?.raw_data?.countries) || []}
|
|
140
140
|
accountAuthenticity={assessmentResult?.account_authenticity}
|
|
141
141
|
companyName={badgeData.companyName}
|
|
142
|
-
|
|
142
|
+
sourcesProviders={connected.map(a => (a?.name || '').toLowerCase())}
|
|
143
|
+
connectedAccounts={connected}
|
|
143
144
|
rightBadgeLayout={!hasEnterpriseMatch}
|
|
144
145
|
/>
|
|
145
146
|
</div>
|
|
@@ -245,14 +245,15 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless, selfCheck = false,
|
|
|
245
245
|
developerName={badgeData.developerName}
|
|
246
246
|
updatedAt={updatedAt}
|
|
247
247
|
score={overallFinalPercent || 0}
|
|
248
|
-
isPublic={
|
|
248
|
+
isPublic={isPublicView}
|
|
249
249
|
badgeImageUrl={badgeData.badgeImageUrl || ''}
|
|
250
250
|
summary={undefined}
|
|
251
251
|
enterpriseMatch={null}
|
|
252
252
|
countries={(assessmentResult?.screening_sources?.ip_risk_analysis?.raw_data?.countries) || []}
|
|
253
253
|
accountAuthenticity={assessmentResult?.account_authenticity}
|
|
254
254
|
companyName={badgeData.companyName}
|
|
255
|
-
sourcesProviders={
|
|
255
|
+
sourcesProviders={connected.map(a => (a?.name || '').toLowerCase())}
|
|
256
|
+
connectedAccounts={connected}
|
|
256
257
|
selfCheck={selfCheck}
|
|
257
258
|
rightBadgeLayout={!hasEnterpriseMatch}
|
|
258
259
|
/>
|
|
@@ -340,54 +341,62 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless, selfCheck = false,
|
|
|
340
341
|
<div className="space-y-10">
|
|
341
342
|
{hasEnterpriseMatch && (
|
|
342
343
|
<div>
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
{
|
|
360
|
-
<
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
344
|
+
{(() => {
|
|
345
|
+
const em = assessmentResult?.enterprise_match;
|
|
346
|
+
if (!em) return null;
|
|
347
|
+
const role = em.role || {};
|
|
348
|
+
const roleName = role?.name || '';
|
|
349
|
+
return (
|
|
350
|
+
<>
|
|
351
|
+
<Reveal headless={isHeadless} as={'h4'} offsetY={8} className={'text-2xl font-semibold mb-3'} style={{ color: 'var(--text-main)' }}>
|
|
352
|
+
Role Alignment{roleName ? ` • ${roleName}` : ''}
|
|
353
|
+
</Reveal>
|
|
354
|
+
<Reveal headless={isHeadless}>
|
|
355
|
+
{(() => {
|
|
356
|
+
const primaryDescription = (role?.description || em.description || '').trim();
|
|
357
|
+
const isLong = primaryDescription.length > 500;
|
|
358
|
+
const visibleText = isLong && !showFullRoleDescription ? `${primaryDescription.slice(0, 500)}…` : primaryDescription;
|
|
359
|
+
return (
|
|
360
|
+
<div className='mt-3' style={{ color: 'var(--text-secondary)' }}>
|
|
361
|
+
<div className={'text-base font-semibold'} style={{ color: 'var(--text-main)' }}>{roleName}</div>
|
|
362
|
+
{primaryDescription ? (
|
|
363
|
+
<div>
|
|
364
|
+
<span>{visibleText}</span>
|
|
365
|
+
{isLong && (
|
|
366
|
+
<button
|
|
367
|
+
type="button"
|
|
368
|
+
onClick={() => setShowFullRoleDescription(v => !v)}
|
|
369
|
+
className={'ml-2 text-xs font-semibold'}
|
|
370
|
+
style={{ color: 'var(--text-main)' }}
|
|
371
|
+
>
|
|
372
|
+
{showFullRoleDescription ? 'See less' : 'See more'}
|
|
373
|
+
</button>
|
|
374
|
+
)}
|
|
375
|
+
</div>
|
|
376
|
+
) : null}
|
|
377
|
+
|
|
378
|
+
{em.description ? (
|
|
379
|
+
<div>
|
|
380
|
+
<div className="flex items-center text-start gap-2 mt-3">
|
|
381
|
+
<div className={'text-base font-semibold'} style={{ color: 'var(--text-main)' }}>Alignment Description</div>
|
|
382
|
+
<span>-</span>
|
|
383
|
+
<div
|
|
384
|
+
className={`px-2 py-1 inline-block rounded-md font-semibold shadow-sm text-[var(--text-main)] border`}
|
|
385
|
+
style={{ borderColor: matchLabelToColor(em.label) }}
|
|
386
|
+
>
|
|
387
|
+
{em.label}
|
|
388
|
+
</div>
|
|
389
|
+
</div>
|
|
390
|
+
<div className={'text-sm'} style={{ color: 'var(--text-secondary)' }}>{em.description}</div>
|
|
391
|
+
</div>
|
|
392
|
+
) : null}
|
|
383
393
|
</div>
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
</Reveal>
|
|
394
|
+
);
|
|
395
|
+
})()}
|
|
396
|
+
</Reveal>
|
|
397
|
+
</>
|
|
398
|
+
);
|
|
399
|
+
})()}
|
|
391
400
|
</div>
|
|
392
401
|
)}
|
|
393
402
|
{hasCoaching && (() => {
|
package/src/chat/ChatWidget.tsx
CHANGED
|
@@ -36,8 +36,8 @@ export default function ChatWidget({ api = '/api/chat', title = 'KYD Bot', hintT
|
|
|
36
36
|
window.addEventListener('resize', check);
|
|
37
37
|
return () => window.removeEventListener('resize', check);
|
|
38
38
|
}, []);
|
|
39
|
-
// Sidebar open state (default
|
|
40
|
-
const [open, setOpen] = useState<boolean>(
|
|
39
|
+
// Sidebar open state (default collapsed)
|
|
40
|
+
const [open, setOpen] = useState<boolean>(false);
|
|
41
41
|
// Avoid hydration mismatches by rendering only after mount
|
|
42
42
|
const [hasMounted, setHasMounted] = useState(false);
|
|
43
43
|
useEffect(() => { setHasMounted(true); }, []);
|
|
@@ -6,6 +6,7 @@ import countriesLib from 'i18n-iso-countries';
|
|
|
6
6
|
import enLocale from 'i18n-iso-countries/langs/en.json';
|
|
7
7
|
import { FiInfo } from 'react-icons/fi';
|
|
8
8
|
import { ProviderIcon } from '../utils/provider';
|
|
9
|
+
import type { ConnectedAccount } from '../types';
|
|
9
10
|
import { green1, green2, green3, green4, green5 } from '../colors';
|
|
10
11
|
|
|
11
12
|
// Register English locale once at module import time
|
|
@@ -56,11 +57,14 @@ interface ReportHeaderProps {
|
|
|
56
57
|
accountAuthenticity?: { label?: string; description?: string };
|
|
57
58
|
companyName?: string;
|
|
58
59
|
sourcesProviders?: string[];
|
|
60
|
+
connectedAccounts?: ConnectedAccount[];
|
|
59
61
|
selfCheck?: boolean;
|
|
60
62
|
rightBadgeLayout?: boolean;
|
|
61
63
|
}
|
|
62
64
|
|
|
63
|
-
const
|
|
65
|
+
const normalizeProvider = (value?: string) => (value || '').toLowerCase().trim();
|
|
66
|
+
|
|
67
|
+
const ReportHeader = ({ badgeId, developerName, updatedAt, score = 0, badgeImageUrl, summary, enterpriseMatch, countries = [], accountAuthenticity, companyName, sourcesProviders = [], connectedAccounts = [], selfCheck, rightBadgeLayout, isPublic }: ReportHeaderProps) => {
|
|
64
68
|
// Use the dynamic image if available, otherwise fall back to the score-based one.
|
|
65
69
|
const finalBadgeImageUrl = badgeImageUrl || getBadgeImageUrl(score || 0, false, selfCheck);
|
|
66
70
|
const tint = hexToRgba(pickTint(score || 0, selfCheck), 0.06);
|
|
@@ -79,6 +83,35 @@ const ReportHeader = ({ badgeId, developerName, updatedAt, score = 0, badgeImage
|
|
|
79
83
|
return true;
|
|
80
84
|
});
|
|
81
85
|
})();
|
|
86
|
+
const sourceAccounts = (() => {
|
|
87
|
+
const map: Record<string, ConnectedAccount> = {};
|
|
88
|
+
(Array.isArray(connectedAccounts) ? connectedAccounts : []).forEach((account) => {
|
|
89
|
+
const normalized = normalizeProvider(account?.name);
|
|
90
|
+
if (normalized && !map[normalized]) map[normalized] = account;
|
|
91
|
+
const underscored = normalized.replace(/\s+/g, '_');
|
|
92
|
+
if (underscored && !map[underscored]) map[underscored] = account;
|
|
93
|
+
const spaced = normalized.replace(/_/g, ' ');
|
|
94
|
+
if (spaced && !map[spaced]) map[spaced] = account;
|
|
95
|
+
});
|
|
96
|
+
return map;
|
|
97
|
+
})();
|
|
98
|
+
const resolveSourceLink = (provider: string) => {
|
|
99
|
+
if (!provider) return null;
|
|
100
|
+
if (!isPublic) {
|
|
101
|
+
return { href: '#appendix-connected', external: false };
|
|
102
|
+
}
|
|
103
|
+
const normalized = normalizeProvider(provider);
|
|
104
|
+
const account = sourceAccounts[normalized] || sourceAccounts[normalized.replace(/\s+/g, '_')] || sourceAccounts[normalized.replace(/_/g, ' ')];
|
|
105
|
+
if (account?.url) {
|
|
106
|
+
return { href: account.url, external: true, providerName: account?.name || provider };
|
|
107
|
+
}
|
|
108
|
+
return null;
|
|
109
|
+
};
|
|
110
|
+
const SourceIcon = ({ provider }: { provider: string }) => (
|
|
111
|
+
<span className={'text-sm'} style={{ color: 'var(--text-main)' }}>
|
|
112
|
+
<ProviderIcon name={provider} className={'inline-block align-middle'} />
|
|
113
|
+
</span>
|
|
114
|
+
);
|
|
82
115
|
|
|
83
116
|
return (
|
|
84
117
|
<div
|
|
@@ -126,18 +159,48 @@ const ReportHeader = ({ badgeId, developerName, updatedAt, score = 0, badgeImage
|
|
|
126
159
|
);
|
|
127
160
|
})()}
|
|
128
161
|
{sources.length > 0 && (
|
|
129
|
-
|
|
130
|
-
<
|
|
162
|
+
isPublic ? (
|
|
163
|
+
<div className='flex flex-wrap items-center gap-2 mt-2 text-sm' style={{ color: 'var(--text-secondary)' }}>
|
|
131
164
|
<span className={'font-semibold'}>Sources Shared:</span>
|
|
132
|
-
<span className={'flex items-center gap-2'}>
|
|
133
|
-
{sources.map((provider, idx) =>
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
165
|
+
<span className={'flex items-center gap-2 flex-wrap'}>
|
|
166
|
+
{sources.map((provider, idx) => {
|
|
167
|
+
const link = resolveSourceLink(provider);
|
|
168
|
+
const icon = <SourceIcon provider={provider} />;
|
|
169
|
+
if (link) {
|
|
170
|
+
return (
|
|
171
|
+
<a
|
|
172
|
+
key={`${provider}-${idx}`}
|
|
173
|
+
href={link.href}
|
|
174
|
+
target={link.external ? '_blank' : undefined}
|
|
175
|
+
rel={link.external ? 'noreferrer noopener' : undefined}
|
|
176
|
+
className={'inline-flex items-center'}
|
|
177
|
+
style={{ color: 'var(--text-main)' }}
|
|
178
|
+
title={link.providerName || provider}
|
|
179
|
+
>
|
|
180
|
+
{icon}
|
|
181
|
+
</a>
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
return (
|
|
185
|
+
<span key={`${provider}-${idx}`} className={'inline-flex items-center'}>
|
|
186
|
+
{icon}
|
|
187
|
+
</span>
|
|
188
|
+
);
|
|
189
|
+
})}
|
|
138
190
|
</span>
|
|
139
|
-
</
|
|
140
|
-
|
|
191
|
+
</div>
|
|
192
|
+
) : (
|
|
193
|
+
<p className='flex items-center gap-2 mt-2 text-sm'>
|
|
194
|
+
<a href="#appendix-connected" className={'inline-flex items-center gap-2 group'} style={{ color: 'var(--text-secondary)' }}>
|
|
195
|
+
<span className={'font-semibold'}>Sources Shared:</span>
|
|
196
|
+
<span className={'flex items-center gap-2'}>
|
|
197
|
+
{sources.map((provider, idx) => (
|
|
198
|
+
<SourceIcon key={`${provider}-${idx}`} provider={provider} />
|
|
199
|
+
))}
|
|
200
|
+
</span>
|
|
201
|
+
</a>
|
|
202
|
+
</p>
|
|
203
|
+
)
|
|
141
204
|
)}
|
|
142
205
|
{(enterpriseMatch?.description || matchLabel) ? (
|
|
143
206
|
<div className={'hidden md:block text-sm space-y-2 pt-4'} style={{ borderTop: '1px solid var(--icon-button-secondary)' }}>
|