kyd-shared-badge 0.1.1 → 0.1.3
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
|
@@ -30,9 +30,10 @@ interface ScoreCardProps {
|
|
|
30
30
|
descriptor?: string;
|
|
31
31
|
icon: IconType;
|
|
32
32
|
scoreType: 'number' | 'risk' | 'descriptor';
|
|
33
|
+
isRecruiter?: boolean;
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
const ScoreCard = ({ title, score, description, descriptor, icon: Icon, scoreType }: ScoreCardProps) => {
|
|
36
|
+
const ScoreCard = ({ title, score, description, descriptor, icon: Icon, scoreType, isRecruiter = false }: ScoreCardProps) => {
|
|
36
37
|
const scoreColor = scoreType === 'descriptor' ? 'text-neutral-400' : getScoreColor(score);
|
|
37
38
|
let displayScore;
|
|
38
39
|
|
|
@@ -45,13 +46,20 @@ const ScoreCard = ({ title, score, description, descriptor, icon: Icon, scoreTyp
|
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
return (
|
|
48
|
-
<div
|
|
49
|
+
<div
|
|
50
|
+
className={
|
|
51
|
+
isRecruiter
|
|
52
|
+
? 'p-6 rounded-xl flex flex-col items-center justify-start text-center shadow h-full border'
|
|
53
|
+
: 'p-6 rounded-lg flex flex-col items-center justify-start text-center dark:bg-neutral-800/50 backdrop-blur-sm bg-white/70 shadow-lg border border-neutral-200 dark:border-neutral-700 h-full'
|
|
54
|
+
}
|
|
55
|
+
style={isRecruiter ? { backgroundColor: 'var(--content-card-background)', borderColor: 'var(--icon-button-secondary)' } : undefined}
|
|
56
|
+
>
|
|
49
57
|
<div className={`text-3xl mb-4 ${scoreColor}`}>
|
|
50
58
|
<Icon />
|
|
51
59
|
</div>
|
|
52
|
-
<h3 className={`font-semibold text-xl
|
|
60
|
+
<h3 className={`font-semibold text-xl ${scoreType === 'descriptor' ? 'text-neutral-400' : ''}`} style={isRecruiter ? { color: 'var(--text-main)' } : undefined}>{title}</h3>
|
|
53
61
|
<p className={`text-4xl font-bold ${scoreColor}`}>{displayScore}</p>
|
|
54
|
-
<p className=
|
|
62
|
+
<p className={isRecruiter ? 'text-sm mt-4' : 'text-sm text-neutral-600 dark:text-neutral-400 mt-4'} style={isRecruiter ? { color: 'var(--text-secondary)' } : undefined}>{description}</p>
|
|
55
63
|
</div>
|
|
56
64
|
);
|
|
57
65
|
};
|
|
@@ -65,6 +73,7 @@ const SharedBadgeDisplay = ({ badgeData, type = 'individual' }: { badgeData: Pub
|
|
|
65
73
|
const aiUsageScore = summary_scores.ai_usage;
|
|
66
74
|
|
|
67
75
|
const wrapperMaxWidth = type === 'recruiter' ? 'max-w-5xl' : 'max-w-6xl';
|
|
76
|
+
const isRecruiter = type === 'recruiter';
|
|
68
77
|
|
|
69
78
|
return (
|
|
70
79
|
<div className={`${wrapperMaxWidth} mx-auto`}>
|
|
@@ -74,7 +83,11 @@ const SharedBadgeDisplay = ({ badgeData, type = 'individual' }: { badgeData: Pub
|
|
|
74
83
|
shareTitle={`KYD Self-Check™ Report | ${badgeData.developerName}`}
|
|
75
84
|
shareText="Check out my KYD Self-Check™ from Know Your Developer™"
|
|
76
85
|
buttonText="Share"
|
|
77
|
-
className=
|
|
86
|
+
className={
|
|
87
|
+
isRecruiter
|
|
88
|
+
? 'flex items-center justify-center h-10 w-10 sm:w-auto sm:px-4 sm:py-2 text-white rounded-full sm:rounded-md transition-all duration-300 ease-in-out bg-[var(--icon-accent)] hover:bg-[var(--icon-accent-hover)]'
|
|
89
|
+
: 'flex items-center justify-center h-10 w-10 sm:w-auto sm:px-4 sm:py-2 bg-indigo-600 text-white rounded-full sm:rounded-md hover:bg-indigo-700 transition-all duration-300 ease-in-out'
|
|
90
|
+
}
|
|
78
91
|
/>
|
|
79
92
|
</div>
|
|
80
93
|
<ReportHeader
|
|
@@ -84,8 +97,16 @@ const SharedBadgeDisplay = ({ badgeData, type = 'individual' }: { badgeData: Pub
|
|
|
84
97
|
score={summary_scores.kyd_self_check.score}
|
|
85
98
|
isPublic={true}
|
|
86
99
|
badgeImageUrl={badgeData.badgeImageUrl || ''}
|
|
100
|
+
type={type}
|
|
87
101
|
/>
|
|
88
|
-
<div
|
|
102
|
+
<div
|
|
103
|
+
className={
|
|
104
|
+
isRecruiter
|
|
105
|
+
? 'rounded-xl shadow-xl p-6 sm:p-8 mt-8 border'
|
|
106
|
+
: 'bg-white/80 dark:bg-neutral-900/80 backdrop-blur-sm rounded-lg shadow-xl p-6 sm:p-8 mt-8'
|
|
107
|
+
}
|
|
108
|
+
style={isRecruiter ? { backgroundColor: 'var(--content-card-background)', borderColor: 'var(--icon-button-secondary)' } : undefined}
|
|
109
|
+
>
|
|
89
110
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-12">
|
|
90
111
|
<ScoreCard
|
|
91
112
|
title="KYD Technical™"
|
|
@@ -93,6 +114,7 @@ const SharedBadgeDisplay = ({ badgeData, type = 'individual' }: { badgeData: Pub
|
|
|
93
114
|
description={devTrustScore?.description || ''}
|
|
94
115
|
icon={FiThumbsUp}
|
|
95
116
|
scoreType='number'
|
|
117
|
+
isRecruiter={isRecruiter}
|
|
96
118
|
/>
|
|
97
119
|
<ScoreCard
|
|
98
120
|
title="KYD Risk™"
|
|
@@ -100,6 +122,7 @@ const SharedBadgeDisplay = ({ badgeData, type = 'individual' }: { badgeData: Pub
|
|
|
100
122
|
description={riskScore?.description || ''}
|
|
101
123
|
icon={FiShield}
|
|
102
124
|
scoreType='risk'
|
|
125
|
+
isRecruiter={isRecruiter}
|
|
103
126
|
/>
|
|
104
127
|
<ScoreCard
|
|
105
128
|
title={<span>KYD AI™ <span className="text-xs font-semibold bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200 px-2 py-1 rounded-full align-middle">Beta</span></span>}
|
|
@@ -108,21 +131,22 @@ const SharedBadgeDisplay = ({ badgeData, type = 'individual' }: { badgeData: Pub
|
|
|
108
131
|
descriptor={aiUsageScore?.descriptor}
|
|
109
132
|
icon={FiCpu}
|
|
110
133
|
scoreType='descriptor'
|
|
134
|
+
isRecruiter={isRecruiter}
|
|
111
135
|
/>
|
|
112
136
|
</div>
|
|
113
137
|
|
|
114
|
-
<div className=
|
|
138
|
+
<div className={isRecruiter ? 'space-y-12 divide-y' : 'space-y-12 divide-y divide-neutral-200/50 dark:divide-neutral-700/50'} style={isRecruiter ? { borderColor: 'var(--icon-button-secondary)' } : undefined}>
|
|
115
139
|
<div className="pt-8 first:pt-0">
|
|
116
|
-
<h3 className=
|
|
117
|
-
<div className=
|
|
140
|
+
<h3 className={isRecruiter ? 'text-2xl font-bold mb-4' : 'text-2xl font-bold text-neutral-900 dark:text-white mb-4'} style={isRecruiter ? { color: 'var(--text-main)' } : undefined}>1. Summary Findings</h3>
|
|
141
|
+
<div className={isRecruiter ? 'prose prose-sm max-w-none' : 'prose prose-sm dark:prose-invert max-w-none text-neutral-600 dark:text-neutral-300'} style={isRecruiter ? { color: 'var(--text-secondary)' } : undefined}>
|
|
118
142
|
<p>{report_summary}</p>
|
|
119
143
|
</div>
|
|
120
144
|
{key_skills && key_skills.length > 0 && (
|
|
121
145
|
<div className="mt-6">
|
|
122
|
-
<h4 className=
|
|
146
|
+
<h4 className={isRecruiter ? 'text-lg font-semibold mb-3' : 'text-lg font-semibold text-neutral-900 dark:text-white mb-3'} style={isRecruiter ? { color: 'var(--text-main)' } : undefined}>Key Skills Observed</h4>
|
|
123
147
|
<div className="flex flex-wrap gap-2">
|
|
124
148
|
{key_skills.map((skill: string, index: number) => (
|
|
125
|
-
<span key={index} className=
|
|
149
|
+
<span key={index} className={isRecruiter ? 'text-xs font-medium px-2.5 py-1 rounded-full' : 'bg-blue-100 text-blue-800 text-xs font-medium px-2.5 py-1 rounded-full dark:bg-blue-900 dark:text-blue-300'} style={isRecruiter ? { backgroundColor: 'var(--icon-button-secondary)', color: 'var(--text-main)' } : undefined}>
|
|
126
150
|
{skill}
|
|
127
151
|
</span>
|
|
128
152
|
))}
|
|
@@ -132,8 +156,8 @@ const SharedBadgeDisplay = ({ badgeData, type = 'individual' }: { badgeData: Pub
|
|
|
132
156
|
</div>
|
|
133
157
|
|
|
134
158
|
<div className="pt-8">
|
|
135
|
-
<h3 className=
|
|
136
|
-
<div className=
|
|
159
|
+
<h3 className={isRecruiter ? 'text-xl font-bold mb-3' : 'text-xl font-bold text-neutral-900 dark:text-white mb-3'} style={isRecruiter ? { color: 'var(--text-main)' } : undefined}>2. KYD Technical™ Signals</h3>
|
|
160
|
+
<div className={isRecruiter ? 'prose prose-sm max-w-none mb-6' : 'prose prose-sm dark:prose-invert max-w-none text-neutral-600 dark:text-neutral-300 mb-6'} style={isRecruiter ? { color: 'var(--text-secondary)' } : undefined}>
|
|
137
161
|
<p>{developer_trust_explanation}</p>
|
|
138
162
|
</div>
|
|
139
163
|
<ProviderInsights
|
|
@@ -144,16 +168,16 @@ const SharedBadgeDisplay = ({ badgeData, type = 'individual' }: { badgeData: Pub
|
|
|
144
168
|
</div>
|
|
145
169
|
|
|
146
170
|
<div className="pt-8">
|
|
147
|
-
<h3 className=
|
|
171
|
+
<h3 className={isRecruiter ? 'text-xl font-bold mb-3' : 'text-xl font-bold text-neutral-900 dark:text-white mb-3'} style={isRecruiter ? { color: 'var(--text-main)' } : undefined}>3. KYD Risk™ Signals</h3>
|
|
148
172
|
{badgeData.optOutScreening ? (
|
|
149
|
-
<div className=
|
|
173
|
+
<div className={isRecruiter ? 'mb-4 p-4 rounded-lg border' : 'mb-4 p-4 bg-yellow-100 dark:bg-yellow-900/30 border border-yellow-300 dark:border-yellow-700 rounded-lg'} style={isRecruiter ? { backgroundColor: 'var(--icon-button-secondary)', borderColor: 'var(--icon-button-secondary)' } : undefined}>
|
|
150
174
|
<div className="flex items-start">
|
|
151
175
|
<span className="h-5 w-5 text-yellow-500 dark:text-yellow-400 mr-3 mt-0.5 flex-shrink-0">
|
|
152
176
|
<FiAlertTriangle size={20} />
|
|
153
177
|
</span>
|
|
154
178
|
<div>
|
|
155
|
-
<h4 className=
|
|
156
|
-
<p className=
|
|
179
|
+
<h4 className={isRecruiter ? 'font-bold' : 'font-bold text-yellow-800 dark:text-yellow-200'} style={isRecruiter ? { color: 'var(--text-main)' } : undefined}>User Opted Out of Screening</h4>
|
|
180
|
+
<p className={isRecruiter ? 'text-sm mt-1' : 'text-sm text-yellow-700 dark:text-yellow-300 mt-1'} style={isRecruiter ? { color: 'var(--text-secondary)' } : undefined}>
|
|
157
181
|
The user chose not to participate in the automated sanctions and risk screening process. The risk score reflects this decision and is for informational purposes only.
|
|
158
182
|
</p>
|
|
159
183
|
</div>
|
|
@@ -161,7 +185,7 @@ const SharedBadgeDisplay = ({ badgeData, type = 'individual' }: { badgeData: Pub
|
|
|
161
185
|
</div>
|
|
162
186
|
) : (
|
|
163
187
|
<>
|
|
164
|
-
<div className=
|
|
188
|
+
<div className={isRecruiter ? 'prose prose-sm max-w-none space-y-4 mb-6' : 'prose prose-sm dark:prose-invert max-w-none text-neutral-600 dark:text-neutral-300 space-y-4 mb-6'} style={isRecruiter ? { color: 'var(--text-secondary)' } : undefined}>
|
|
165
189
|
<p>{riskScore?.description || ''}</p>
|
|
166
190
|
</div>
|
|
167
191
|
<IpRiskAnalysisDisplay ipRiskAnalysis={screening_sources?.ip_risk_analysis} />
|
|
@@ -170,8 +194,8 @@ const SharedBadgeDisplay = ({ badgeData, type = 'individual' }: { badgeData: Pub
|
|
|
170
194
|
</div>
|
|
171
195
|
|
|
172
196
|
<div className="pt-8">
|
|
173
|
-
<h3 className=
|
|
174
|
-
<div className=
|
|
197
|
+
<h3 className={isRecruiter ? 'text-xl font-bold mb-3' : 'text-xl font-bold text-neutral-900 dark:text-white mb-3'} style={isRecruiter ? { color: 'var(--text-main)' } : undefined}>4. KYD AI™ Signals <span className="text-sm font-semibold bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200 px-2 py-1 rounded-full align-middle">Beta</span></h3>
|
|
198
|
+
<div className={isRecruiter ? 'prose prose-sm max-w-none mb-6 space-y-4' : 'prose prose-sm dark:prose-invert max-w-none text-neutral-600 dark:text-neutral-300 mb-6 space-y-4'} style={isRecruiter ? { color: 'var(--text-secondary)' } : undefined}>
|
|
175
199
|
<p>{assessmentResult.ai_usage_summary?.explanation}</p>
|
|
176
200
|
{assessmentResult.ai_usage_summary?.key_findings && (
|
|
177
201
|
<ul className="list-disc list-inside">
|
|
@@ -184,39 +208,41 @@ const SharedBadgeDisplay = ({ badgeData, type = 'individual' }: { badgeData: Pub
|
|
|
184
208
|
</div>
|
|
185
209
|
|
|
186
210
|
<div className="pt-8">
|
|
187
|
-
<h3 className=
|
|
188
|
-
<div className=
|
|
211
|
+
<h3 className={isRecruiter ? 'text-xl font-bold mb-3' : 'text-xl font-bold text-neutral-900 dark:text-white mb-3'} style={isRecruiter ? { color: 'var(--text-main)' } : undefined}>5. Industry Considerations</h3>
|
|
212
|
+
<div className={isRecruiter ? 'prose prose-sm max-w-none' : 'prose prose-sm dark:prose-invert max-w-none text-neutral-600 dark:text-neutral-300'} style={isRecruiter ? { color: 'var(--text-secondary)' } : undefined}>
|
|
189
213
|
<p>{industry_considerations}</p>
|
|
190
214
|
</div>
|
|
191
215
|
</div>
|
|
192
216
|
|
|
193
217
|
{!badgeData.optOutScreening && screening_sources && (
|
|
194
218
|
<div className="pt-8">
|
|
195
|
-
<h3 className=
|
|
219
|
+
<h3 className={isRecruiter ? 'text-2xl font-bold mb-4' : 'text-2xl font-bold text-neutral-900 dark:text-white mb-4'} style={isRecruiter ? { color: 'var(--text-main)' } : undefined}>6. Appendix: Data Sources</h3>
|
|
196
220
|
<div className="space-y-8">
|
|
197
221
|
<div>
|
|
198
|
-
<h4 className=
|
|
222
|
+
<h4 className={isRecruiter ? 'text-xl font-bold mb-4' : 'text-xl font-bold text-neutral-800 dark:text-white mb-4'} style={isRecruiter ? { color: 'var(--text-main)' } : undefined}>Sanctions & Watchlists</h4>
|
|
199
223
|
<AppendixTables
|
|
200
224
|
type="sanctions"
|
|
201
225
|
sources={[...(screening_sources.ofac_lists || []), ...(screening_sources.additional_watchlists || [])]}
|
|
202
226
|
searchedAt={updatedAt}
|
|
203
227
|
developerName={developerName || 'this developer'}
|
|
228
|
+
view={type}
|
|
204
229
|
/>
|
|
205
230
|
</div>
|
|
206
231
|
<div>
|
|
207
|
-
<h4 className=
|
|
232
|
+
<h4 className={isRecruiter ? 'text-xl font-bold mb-4' : 'text-xl font-bold text-neutral-800 dark:text-white mb-4'} style={isRecruiter ? { color: 'var(--text-main)' } : undefined}>Country-specific Entity Affiliations</h4>
|
|
208
233
|
<AppendixTables
|
|
209
234
|
type="domains"
|
|
210
235
|
sources={screening_sources.risk_profile_domains || []}
|
|
211
236
|
searchedAt={updatedAt}
|
|
212
237
|
developerName={developerName || 'this developer'}
|
|
238
|
+
view={type}
|
|
213
239
|
/>
|
|
214
240
|
</div>
|
|
215
241
|
</div>
|
|
216
242
|
</div>
|
|
217
243
|
)}
|
|
218
244
|
|
|
219
|
-
<div className=
|
|
245
|
+
<div className={isRecruiter ? 'pt-8 text-sm text-center' : 'pt-8 text-sm text-neutral-500 dark:text-neutral-400 text-center'} style={isRecruiter ? { color: 'var(--text-secondary)' } : undefined}>
|
|
220
246
|
Report Completed: {new Date(updatedAt).toLocaleString(undefined, {
|
|
221
247
|
year: 'numeric',
|
|
222
248
|
month: 'long',
|
|
@@ -228,8 +254,8 @@ const SharedBadgeDisplay = ({ badgeData, type = 'individual' }: { badgeData: Pub
|
|
|
228
254
|
</div>
|
|
229
255
|
</div>
|
|
230
256
|
</div>
|
|
231
|
-
<footer className=
|
|
232
|
-
<p className=
|
|
257
|
+
<footer className={isRecruiter ? 'mt-12 pt-6 border-t' : 'mt-12 pt-6 border-t border-neutral-200 dark:border-neutral-700'} style={isRecruiter ? { borderColor: 'var(--icon-button-secondary)' } : undefined}>
|
|
258
|
+
<p className={isRecruiter ? 'text-center text-xs max-w-4xl mx-auto' : 'text-center text-xs text-gray-500 dark:text-gray-400 max-w-4xl mx-auto'} style={isRecruiter ? { color: 'var(--text-secondary)' } : undefined}>
|
|
233
259
|
© 2025 Know Your Developer, LLC. All rights reserved. KYD Self-Check™, and associated marks are trademarks of Know Your Developer, LLC. This document is confidential, proprietary, and intended solely for the individual or entity to whom it is addressed. Unauthorized use, disclosure, copying, or distribution of this document or any of its contents is strictly prohibited and may be unlawful. Know Your Developer, LLC assumes no responsibility or liability for any errors or omissions contained herein. Report validity subject to the terms and conditions stated on the official Know Your Developer website located at https://knowyourdeveloper.ai.
|
|
234
260
|
</p>
|
|
235
261
|
</footer>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import React, { useState } from 'react';
|
|
4
|
-
import { DomainCSVRow } from '../
|
|
4
|
+
import { DomainCSVRow } from '../types';
|
|
5
5
|
|
|
6
6
|
interface SanctionSource {
|
|
7
7
|
issuingEntity: string;
|
|
@@ -22,51 +22,54 @@ interface AppendixTableProps {
|
|
|
22
22
|
sources: string[] | DomainCSVRow[];
|
|
23
23
|
searchedAt: string;
|
|
24
24
|
developerName: string;
|
|
25
|
+
view?: 'recruiter' | 'individual';
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
const SanctionsRow = ({ source, searchedAt, developerName }: { source: SanctionSource, searchedAt: string, developerName: string }) => (
|
|
28
|
-
<tr className=
|
|
29
|
-
<td className=
|
|
28
|
+
const SanctionsRow = ({ source, searchedAt, developerName, isRecruiter }: { source: SanctionSource, searchedAt: string, developerName: string, isRecruiter: boolean }) => (
|
|
29
|
+
<tr className={isRecruiter ? 'transition-colors hover:bg-black/5' : 'hover:bg-neutral-50 dark:hover:bg-neutral-800/50 transition-colors'}>
|
|
30
|
+
<td className={isRecruiter ? 'px-4 py-4 whitespace-nowrap text-sm font-medium' : 'px-4 py-4 whitespace-nowrap text-sm font-medium text-neutral-800 dark:text-neutral-200'} style={isRecruiter ? { color: 'var(--text-main)' } : undefined}>
|
|
30
31
|
{source.issuingEntity}
|
|
31
32
|
</td>
|
|
32
|
-
<td className=
|
|
33
|
+
<td className={isRecruiter ? 'px-4 py-4 whitespace-normal text-sm' : 'px-4 py-4 whitespace-normal text-sm text-neutral-600 dark:text-neutral-300'} style={isRecruiter ? { color: 'var(--text-secondary)' } : undefined}>
|
|
33
34
|
{source.listName}
|
|
34
35
|
</td>
|
|
35
|
-
<td className=
|
|
36
|
+
<td className={isRecruiter ? 'px-4 py-4 whitespace-nowrap text-sm' : 'px-4 py-4 whitespace-nowrap text-sm text-neutral-600 dark:text-neutral-300'} style={isRecruiter ? { color: 'var(--text-secondary)' } : undefined}>
|
|
36
37
|
{searchedAt}
|
|
37
38
|
</td>
|
|
38
|
-
<td className=
|
|
39
|
-
<span className=
|
|
39
|
+
<td className={isRecruiter ? 'px-4 py-4 whitespace-nowrap text-sm' : 'px-4 py-4 whitespace-nowrap text-sm text-neutral-600 dark:text-neutral-300'} style={isRecruiter ? { color: 'var(--text-secondary)' } : undefined}>
|
|
40
|
+
<span className={isRecruiter ? 'px-2 inline-flex text-xs leading-5 font-semibold rounded-full' : 'px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200'} style={isRecruiter ? { backgroundColor: 'var(--icon-button-secondary)', color: 'var(--text-main)' } : undefined}>
|
|
40
41
|
Not Found
|
|
41
42
|
</span>
|
|
42
43
|
</td>
|
|
43
|
-
<td className=
|
|
44
|
-
No exact match for <strong>{developerName}</strong> (based on name and email) was found on this list.
|
|
44
|
+
<td className={isRecruiter ? 'px-4 py-4 text-sm whitespace-normal' : 'px-4 py-4 text-sm text-neutral-600 dark:text-neutral-300 whitespace-normal'} style={isRecruiter ? { color: 'var(--text-secondary)' } : undefined}>
|
|
45
|
+
No exact match for <strong style={isRecruiter ? { color: 'var(--text-main)' } : undefined}>{developerName}</strong> (based on name and email) was found on this list.
|
|
45
46
|
</td>
|
|
46
47
|
</tr>
|
|
47
48
|
);
|
|
48
49
|
|
|
49
|
-
const DomainRow = ({ source, searchedAt, developerName }: { source: DomainSource, searchedAt: string, developerName: string }) => (
|
|
50
|
-
<tr className=
|
|
51
|
-
<td className=
|
|
52
|
-
<td className=
|
|
53
|
-
<td className=
|
|
54
|
-
<td className=
|
|
55
|
-
<td className=
|
|
56
|
-
<span className=
|
|
50
|
+
const DomainRow = ({ source, searchedAt, developerName, isRecruiter }: { source: DomainSource, searchedAt: string, developerName: string, isRecruiter: boolean }) => (
|
|
51
|
+
<tr className={isRecruiter ? 'transition-colors hover:bg-black/5' : 'hover:bg-neutral-50 dark:hover:bg-neutral-800/50 transition-colors'}>
|
|
52
|
+
<td className={isRecruiter ? 'px-4 py-4 whitespace-normal text-sm' : 'px-4 py-4 whitespace-normal text-sm text-neutral-600 dark:text-neutral-300'} style={isRecruiter ? { color: 'var(--text-secondary)' } : undefined}>{source.country || 'N/A'}</td>
|
|
53
|
+
<td className={isRecruiter ? 'px-4 py-4 whitespace-normal text-sm' : 'px-4 py-4 whitespace-normal text-sm text-neutral-600 dark:text-neutral-300'} style={isRecruiter ? { color: 'var(--text-secondary)' } : undefined}>{source.entityType || 'N/A'}</td>
|
|
54
|
+
<td className={isRecruiter ? 'px-4 py-4 whitespace-normal text-sm font-medium' : 'px-4 py-4 whitespace-normal text-sm font-medium text-neutral-800 dark:text-neutral-200'} style={isRecruiter ? { color: 'var(--text-main)' } : undefined}>{source.entityName || source.url}</td>
|
|
55
|
+
<td className={isRecruiter ? 'px-4 py-4 whitespace-nowrap text-sm' : 'px-4 py-4 whitespace-nowrap text-sm text-neutral-600 dark:text-neutral-300'} style={isRecruiter ? { color: 'var(--text-secondary)' } : undefined}>{searchedAt}</td>
|
|
56
|
+
<td className={isRecruiter ? 'px-4 py-4 whitespace-nowrap text-sm' : 'px-4 py-4 whitespace-nowrap text-sm text-neutral-600 dark:text-neutral-300'} style={isRecruiter ? { color: 'var(--text-secondary)' } : undefined}>
|
|
57
|
+
<span className={isRecruiter ? 'px-2 inline-flex text-xs leading-5 font-semibold rounded-full' : 'px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200'} style={isRecruiter ? { backgroundColor: 'var(--icon-button-secondary)', color: 'var(--text-main)' } : undefined}>
|
|
57
58
|
Not Found
|
|
58
59
|
</span>
|
|
59
60
|
</td>
|
|
60
|
-
<td className=
|
|
61
|
-
No profile matching <strong>{developerName}</strong> (based on name and email) was found at this domain.
|
|
61
|
+
<td className={isRecruiter ? 'px-4 py-4 text-sm whitespace-normal' : 'px-4 py-4 text-sm text-neutral-600 dark:text-neutral-300 whitespace-normal'} style={isRecruiter ? { color: 'var(--text-secondary)' } : undefined}>
|
|
62
|
+
No profile matching <strong style={isRecruiter ? { color: 'var(--text-main)' } : undefined}>{developerName}</strong> (based on name and email) was found at this domain.
|
|
62
63
|
</td>
|
|
63
64
|
</tr>
|
|
64
65
|
);
|
|
65
66
|
|
|
66
67
|
|
|
67
|
-
const AppendixTables: React.FC<AppendixTableProps> = ({ type, sources, searchedAt, developerName }) => {
|
|
68
|
+
const AppendixTables: React.FC<AppendixTableProps> = ({ type, sources, searchedAt, developerName, view = 'individual' }) => {
|
|
68
69
|
const [visibleCount, setVisibleCount] = useState(PAGE_SIZE);
|
|
69
70
|
|
|
71
|
+
const isRecruiter = view === 'recruiter';
|
|
72
|
+
|
|
70
73
|
const formattedDate = new Date(searchedAt).toLocaleString(undefined, {
|
|
71
74
|
year: 'numeric', month: 'short', day: 'numeric',
|
|
72
75
|
hour: 'numeric', minute: '2-digit',
|
|
@@ -100,35 +103,36 @@ const AppendixTables: React.FC<AppendixTableProps> = ({ type, sources, searchedA
|
|
|
100
103
|
|
|
101
104
|
return (
|
|
102
105
|
<div>
|
|
103
|
-
<div className=
|
|
104
|
-
<table className=
|
|
105
|
-
<thead className=
|
|
106
|
+
<div className={isRecruiter ? 'overflow-x-auto rounded-lg border' : 'overflow-x-auto rounded-lg border border-neutral-200 dark:border-neutral-700'} style={isRecruiter ? { borderColor: 'var(--icon-button-secondary)', backgroundColor: 'var(--content-card-background)' } : undefined}>
|
|
107
|
+
<table className={isRecruiter ? 'min-w-full' : 'min-w-full divide-y divide-neutral-200 dark:divide-neutral-700'}>
|
|
108
|
+
<thead className={isRecruiter ? '' : 'bg-neutral-50 dark:bg-neutral-800'}>
|
|
106
109
|
<tr>
|
|
107
110
|
{headers.map(header => (
|
|
108
|
-
<th key={header} scope="col" className=
|
|
111
|
+
<th key={header} scope="col" className={isRecruiter ? 'px-4 py-3 text-left text-xs font-semibold uppercase tracking-wider' : 'px-4 py-3 text-left text-xs font-semibold text-neutral-500 dark:text-neutral-300 uppercase tracking-wider'} style={isRecruiter ? { color: 'var(--text-secondary)' } : undefined}>
|
|
109
112
|
{header}
|
|
110
113
|
</th>
|
|
111
114
|
))}
|
|
112
115
|
</tr>
|
|
113
116
|
</thead>
|
|
114
|
-
<tbody className=
|
|
117
|
+
<tbody className={isRecruiter ? '' : 'bg-white dark:bg-neutral-900 divide-y divide-neutral-200 dark:divide-neutral-700'}>
|
|
115
118
|
{visibleParsedSources.map((source, index) =>
|
|
116
119
|
type === 'sanctions'
|
|
117
|
-
? <SanctionsRow key={index} source={source as SanctionSource} searchedAt={formattedDate} developerName={developerName} />
|
|
118
|
-
: <DomainRow key={index} source={source as DomainSource} searchedAt={formattedDate} developerName={developerName} />
|
|
120
|
+
? <SanctionsRow key={index} source={source as SanctionSource} searchedAt={formattedDate} developerName={developerName} isRecruiter={isRecruiter} />
|
|
121
|
+
: <DomainRow key={index} source={source as DomainSource} searchedAt={formattedDate} developerName={developerName} isRecruiter={isRecruiter} />
|
|
119
122
|
)}
|
|
120
123
|
</tbody>
|
|
121
124
|
</table>
|
|
122
125
|
</div>
|
|
123
126
|
{parsedSources.length > PAGE_SIZE && (
|
|
124
|
-
<div className=
|
|
127
|
+
<div className={isRecruiter ? 'mt-4 flex items-center justify-between text-sm' : 'mt-4 flex items-center justify-between text-sm text-neutral-600 dark:text-neutral-400'} style={isRecruiter ? { color: 'var(--text-secondary)' } : undefined}>
|
|
125
128
|
<p>
|
|
126
129
|
Showing {Math.min(visibleCount, parsedSources.length)} of {parsedSources.length} entries
|
|
127
130
|
</p>
|
|
128
131
|
{visibleCount < parsedSources.length && (
|
|
129
132
|
<button
|
|
130
133
|
onClick={handleLoadMore}
|
|
131
|
-
className=
|
|
134
|
+
className={isRecruiter ? 'font-medium underline-offset-2 hover:underline' : 'font-medium text-blue-600 hover:text-blue-500 dark:text-blue-500 dark:hover:text-blue-400 transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 rounded-md'}
|
|
135
|
+
style={isRecruiter ? { color: 'var(--icon-accent)' } : undefined}
|
|
132
136
|
>
|
|
133
137
|
Load More
|
|
134
138
|
</button>
|
|
@@ -15,11 +15,13 @@ interface ReportHeaderProps {
|
|
|
15
15
|
score: number | undefined;
|
|
16
16
|
isPublic: boolean;
|
|
17
17
|
badgeImageUrl: string;
|
|
18
|
+
type?: 'recruiter' | 'individual';
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
const ReportHeader = ({ badgeId, developerName, updatedAt, score = 0, isPublic, badgeImageUrl }: ReportHeaderProps) => {
|
|
21
|
+
const ReportHeader = ({ badgeId, developerName, updatedAt, score = 0, isPublic, badgeImageUrl, type = 'individual' }: ReportHeaderProps) => {
|
|
21
22
|
// Use the dynamic image if available, otherwise fall back to the score-based one.
|
|
22
23
|
const finalBadgeImageUrl = badgeImageUrl || getBadgeImageUrl(score);
|
|
24
|
+
const isRecruiter = type === 'recruiter';
|
|
23
25
|
|
|
24
26
|
const formattedDate = updatedAt ? new Date(updatedAt).toLocaleString(undefined, {
|
|
25
27
|
year: 'numeric',
|
|
@@ -30,32 +32,39 @@ const ReportHeader = ({ badgeId, developerName, updatedAt, score = 0, isPublic,
|
|
|
30
32
|
}) : 'N/A';
|
|
31
33
|
|
|
32
34
|
return (
|
|
33
|
-
<div
|
|
35
|
+
<div
|
|
36
|
+
className={
|
|
37
|
+
isRecruiter
|
|
38
|
+
? 'mb-8 p-6 rounded-xl shadow-lg flex flex-col md:flex-row items-start md:items-center justify-between gap-6 border'
|
|
39
|
+
: 'mb-8 p-6 bg-white/80 dark:bg-neutral-900/80 backdrop-blur-sm rounded-lg shadow-lg flex flex-col md:flex-row items-start md:items-center justify-between gap-6'
|
|
40
|
+
}
|
|
41
|
+
style={isRecruiter ? { backgroundColor: 'var(--content-card-background)', borderColor: 'var(--icon-button-secondary)' } : undefined}
|
|
42
|
+
>
|
|
34
43
|
{/* Left Section */}
|
|
35
44
|
<div className="flex items-center text-left md:text-center">
|
|
36
45
|
<Image src={finalBadgeImageUrl} alt="KYD Badge" width={150} height={150} unoptimized />
|
|
37
46
|
<div className='flex flex-col'>
|
|
38
|
-
<h1 className=
|
|
47
|
+
<h1 className={isRecruiter ? 'font-bold text-lg' : 'font-bold text-lg text-neutral-900 dark:text-white'} style={isRecruiter ? { color: 'var(--text-main)' } : undefined}>
|
|
39
48
|
KYD Self-Check™
|
|
40
49
|
</h1>
|
|
41
|
-
<p className=
|
|
42
|
-
{isPublic ? 'Public Report' : 'Private Report'}
|
|
50
|
+
<p className={isRecruiter ? 'text-sm' : 'text-sm text-neutral-600 dark:text-neutral-400'} style={isRecruiter ? { color: 'var(--text-secondary)' } : undefined}>
|
|
51
|
+
{(isPublic && type === 'individual') ? 'Public Report' : 'Private Report'}
|
|
43
52
|
</p>
|
|
44
53
|
</div>
|
|
45
54
|
</div>
|
|
46
55
|
|
|
47
56
|
{/* Middle Section */}
|
|
48
57
|
<div className="text-left md:text-center">
|
|
49
|
-
<p className=
|
|
50
|
-
<p className=
|
|
58
|
+
<p className={isRecruiter ? 'text-sm' : 'text-sm text-neutral-600 dark:text-neutral-400'} style={isRecruiter ? { color: 'var(--text-secondary)' } : undefined}>Developer</p>
|
|
59
|
+
<p className={isRecruiter ? 'font-semibold text-2xl' : 'font-semibold text-neutral-900 dark:text-white text-2xl'} style={isRecruiter ? { color: 'var(--text-main)' } : undefined}>{developerName || 'N/A'}</p>
|
|
51
60
|
</div>
|
|
52
61
|
|
|
53
62
|
{/* Right Section */}
|
|
54
|
-
<div className=
|
|
55
|
-
<p><span className=
|
|
56
|
-
<p><span className=
|
|
57
|
-
<p><span className=
|
|
58
|
-
<p><span className=
|
|
63
|
+
<div className={isRecruiter ? 'text-left text-sm space-y-1' : 'text-left text-sm text-neutral-600 dark:text-neutral-400 space-y-1'} style={isRecruiter ? { color: 'var(--text-secondary)' } : undefined}>
|
|
64
|
+
<p><span className={isRecruiter ? 'font-semibold' : 'font-semibold text-neutral-800 dark:text-neutral-200'} style={isRecruiter ? { color: 'var(--text-main)' } : undefined}>Requested By:</span> {developerName || 'N/A'}</p>
|
|
65
|
+
<p><span className={isRecruiter ? 'font-semibold' : 'font-semibold text-neutral-800 dark:text-neutral-200'} style={isRecruiter ? { color: 'var(--text-main)' } : undefined}>Organization:</span> Unaffiliated</p>
|
|
66
|
+
<p><span className={isRecruiter ? 'font-semibold' : 'font-semibold text-neutral-800 dark:text-neutral-200'} style={isRecruiter ? { color: 'var(--text-main)' } : undefined}>Date Completed:</span> {formattedDate}</p>
|
|
67
|
+
<p><span className={isRecruiter ? 'font-semibold' : 'font-semibold text-neutral-800 dark:text-neutral-200'} style={isRecruiter ? { color: 'var(--text-main)' } : undefined}>Report ID:</span> {badgeId}</p>
|
|
59
68
|
</div>
|
|
60
69
|
</div>
|
|
61
70
|
);
|