kyd-shared-badge 0.2.2 → 0.2.4
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 +1 -1
- package/src/SharedBadgeDisplay.tsx +41 -52
- package/src/colors.ts +39 -1
- package/src/components/AppendixTables.tsx +30 -33
- package/src/components/IpRiskAnalysisDisplay.tsx +13 -13
- package/src/components/ProviderInsights.tsx +15 -20
- package/src/components/ReportHeader.tsx +28 -19
- package/src/components/ShareButton.tsx +10 -7
package/package.json
CHANGED
|
@@ -17,6 +17,14 @@ const getScoreColor = (score: number) => {
|
|
|
17
17
|
return red;
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
+
const hexToRgba = (hex: string, alpha: number) => {
|
|
21
|
+
const clean = hex.replace('#', '');
|
|
22
|
+
const r = parseInt(clean.substring(0, 2), 16);
|
|
23
|
+
const g = parseInt(clean.substring(2, 4), 16);
|
|
24
|
+
const b = parseInt(clean.substring(4, 6), 16);
|
|
25
|
+
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
|
26
|
+
};
|
|
27
|
+
|
|
20
28
|
const getRiskText = (risk: number) => {
|
|
21
29
|
if (risk >= 80) return 'Low Risk';
|
|
22
30
|
if (risk >= 65) return 'Medium Risk';
|
|
@@ -55,14 +63,14 @@ interface ScoreCardProps {
|
|
|
55
63
|
description: string;
|
|
56
64
|
descriptor?: string;
|
|
57
65
|
scoreType: 'number' | 'risk' | 'descriptor';
|
|
58
|
-
isRecruiter?: boolean;
|
|
59
66
|
iconImageSrc: string;
|
|
60
67
|
}
|
|
61
68
|
|
|
62
|
-
const ScoreCard = ({ title, score, description, descriptor, scoreType,
|
|
69
|
+
const ScoreCard = ({ title, score, description, descriptor, scoreType, iconImageSrc }: ScoreCardProps) => {
|
|
63
70
|
const isDescriptor = scoreType === 'descriptor';
|
|
64
71
|
const scoreHex = isDescriptor ? undefined : getScoreColor(score);
|
|
65
72
|
const scoreClass = isDescriptor ? 'text-neutral-400' : '';
|
|
73
|
+
const overlay = scoreHex ? hexToRgba(scoreHex, 0.08) : 'transparent';
|
|
66
74
|
let displayScore;
|
|
67
75
|
|
|
68
76
|
if (scoreType === 'risk') {
|
|
@@ -76,23 +84,21 @@ const ScoreCard = ({ title, score, description, descriptor, scoreType, isRecruit
|
|
|
76
84
|
return (
|
|
77
85
|
<div
|
|
78
86
|
className={
|
|
79
|
-
|
|
80
|
-
? 'p-6 rounded-xl flex flex-col items-center justify-start text-center shadow h-full border'
|
|
81
|
-
: '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'
|
|
87
|
+
'p-6 rounded-xl flex flex-col items-center justify-start text-center shadow h-full border'
|
|
82
88
|
}
|
|
83
|
-
style={
|
|
89
|
+
style={{ backgroundColor: 'var(--content-card-background)', borderColor: 'var(--icon-button-secondary)', backgroundImage: `linear-gradient(${overlay}, ${overlay})` }}
|
|
84
90
|
>
|
|
85
91
|
<div className={`text-3xl mb-4 ${scoreClass}`} style={scoreHex ? { color: scoreHex } : undefined}>
|
|
86
92
|
<Image src={iconImageSrc} alt="icon" width={32} height={32} className="h-8 w-8 object-contain" />
|
|
87
93
|
</div>
|
|
88
|
-
<h3 className={`font-semibold text-xl ${isDescriptor ? 'text-neutral-400' : ''}`} style={
|
|
94
|
+
<h3 className={`font-semibold text-xl ${isDescriptor ? 'text-neutral-400' : ''}`} style={{ color: 'var(--text-main)' }}>{title}</h3>
|
|
89
95
|
<p className={`text-4xl font-bold ${scoreClass}`} style={scoreHex ? { color: scoreHex } : undefined}>{displayScore}</p>
|
|
90
|
-
<p className={
|
|
96
|
+
<p className={'text-sm text-neutral-600 dark:text-neutral-400 mt-4'} style={{ color: 'var(--text-secondary)' }}>{description}</p>
|
|
91
97
|
</div>
|
|
92
98
|
);
|
|
93
99
|
};
|
|
94
100
|
|
|
95
|
-
const SharedBadgeDisplay = ({ badgeData
|
|
101
|
+
const SharedBadgeDisplay = ({ badgeData }: { badgeData: PublicBadgeData }) => {
|
|
96
102
|
const { badgeId, developerName, assessmentResult, updatedAt, connectedPlatforms } = badgeData;
|
|
97
103
|
const { summary_scores, report_summary, developer_trust_explanation, key_skills, screening_sources, industry_considerations } = assessmentResult;
|
|
98
104
|
|
|
@@ -100,8 +106,7 @@ const SharedBadgeDisplay = ({ badgeData, type = 'individual' }: { badgeData: Pub
|
|
|
100
106
|
const riskScore = summary_scores.risk_score;
|
|
101
107
|
const aiUsageScore = summary_scores.ai_usage;
|
|
102
108
|
|
|
103
|
-
const wrapperMaxWidth =
|
|
104
|
-
const isRecruiter = type === 'recruiter';
|
|
109
|
+
const wrapperMaxWidth = 'max-w-5xl';
|
|
105
110
|
|
|
106
111
|
return (
|
|
107
112
|
<div className={`${wrapperMaxWidth} mx-auto`}>
|
|
@@ -111,11 +116,7 @@ const SharedBadgeDisplay = ({ badgeData, type = 'individual' }: { badgeData: Pub
|
|
|
111
116
|
shareTitle={`KYD Self-Check™ Report | ${badgeData.developerName}`}
|
|
112
117
|
shareText="Check out my KYD Self-Check™ from Know Your Developer™"
|
|
113
118
|
buttonText="Share"
|
|
114
|
-
className={
|
|
115
|
-
isRecruiter
|
|
116
|
-
? '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)]'
|
|
117
|
-
: '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'
|
|
118
|
-
}
|
|
119
|
+
className={'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)]'}
|
|
119
120
|
/>
|
|
120
121
|
</div>
|
|
121
122
|
<ReportHeader
|
|
@@ -125,15 +126,10 @@ const SharedBadgeDisplay = ({ badgeData, type = 'individual' }: { badgeData: Pub
|
|
|
125
126
|
score={summary_scores.kyd_self_check.score}
|
|
126
127
|
isPublic={true}
|
|
127
128
|
badgeImageUrl={badgeData.badgeImageUrl || ''}
|
|
128
|
-
type={type}
|
|
129
129
|
/>
|
|
130
130
|
<div
|
|
131
|
-
className={
|
|
132
|
-
|
|
133
|
-
? 'rounded-xl shadow-xl p-6 sm:p-8 mt-8 border'
|
|
134
|
-
: 'bg-white/80 dark:bg-neutral-900/80 backdrop-blur-sm rounded-lg shadow-xl p-6 sm:p-8 mt-8'
|
|
135
|
-
}
|
|
136
|
-
style={isRecruiter ? { backgroundColor: 'var(--content-card-background)', borderColor: 'var(--icon-button-secondary)' } : undefined}
|
|
131
|
+
className={'rounded-xl shadow-xl p-6 sm:p-8 mt-8 border'}
|
|
132
|
+
style={{ backgroundColor: 'var(--content-card-background)', borderColor: 'var(--icon-button-secondary)' }}
|
|
137
133
|
>
|
|
138
134
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-12">
|
|
139
135
|
<ScoreCard
|
|
@@ -141,7 +137,6 @@ const SharedBadgeDisplay = ({ badgeData, type = 'individual' }: { badgeData: Pub
|
|
|
141
137
|
score={devTrustScore?.score || 0}
|
|
142
138
|
description={devTrustScore?.description || ''}
|
|
143
139
|
scoreType='number'
|
|
144
|
-
isRecruiter={isRecruiter}
|
|
145
140
|
iconImageSrc={getTechnicalIconSrc(devTrustScore?.score || 0)}
|
|
146
141
|
/>
|
|
147
142
|
<ScoreCard
|
|
@@ -149,7 +144,6 @@ const SharedBadgeDisplay = ({ badgeData, type = 'individual' }: { badgeData: Pub
|
|
|
149
144
|
score={riskScore?.score || 0}
|
|
150
145
|
description={riskScore?.description || ''}
|
|
151
146
|
scoreType='risk'
|
|
152
|
-
isRecruiter={isRecruiter}
|
|
153
147
|
iconImageSrc={getRiskIconSrc(riskScore?.score || 0)}
|
|
154
148
|
/>
|
|
155
149
|
<ScoreCard
|
|
@@ -158,23 +152,22 @@ const SharedBadgeDisplay = ({ badgeData, type = 'individual' }: { badgeData: Pub
|
|
|
158
152
|
description={aiUsageScore?.description || 'Analysis of AI tool usage and transparency.'}
|
|
159
153
|
descriptor={aiUsageScore?.descriptor}
|
|
160
154
|
scoreType='descriptor'
|
|
161
|
-
isRecruiter={isRecruiter}
|
|
162
155
|
iconImageSrc={getAiIconSrc(aiUsageScore?.descriptor)}
|
|
163
156
|
/>
|
|
164
157
|
</div>
|
|
165
158
|
|
|
166
|
-
<div className={
|
|
159
|
+
<div className={'space-y-12 divide-y'} style={{ borderColor: 'var(--icon-button-secondary)' }}>
|
|
167
160
|
<div className="pt-8 first:pt-0">
|
|
168
|
-
<h3 className={
|
|
169
|
-
<div className={
|
|
161
|
+
<h3 className={'text-2xl font-bold mb-4'} style={{ color: 'var(--text-main)' }}>1. Summary Findings</h3>
|
|
162
|
+
<div className={'prose prose-sm max-w-none'} style={{ color: 'var(--text-secondary)' }}>
|
|
170
163
|
<p>{report_summary}</p>
|
|
171
164
|
</div>
|
|
172
165
|
{key_skills && key_skills.length > 0 && (
|
|
173
166
|
<div className="mt-6">
|
|
174
|
-
<h4 className={
|
|
167
|
+
<h4 className={'text-lg font-semibold mb-3'} style={{ color: 'var(--text-main)' }}>Key Skills Observed</h4>
|
|
175
168
|
<div className="flex flex-wrap gap-2">
|
|
176
169
|
{key_skills.map((skill: string, index: number) => (
|
|
177
|
-
<span key={index} className={
|
|
170
|
+
<span key={index} className={'text-xs font-medium px-2.5 py-1 rounded-full'} style={{ backgroundColor: 'var(--icon-button-secondary)', color: 'var(--text-main)' }}>
|
|
178
171
|
{skill}
|
|
179
172
|
</span>
|
|
180
173
|
))}
|
|
@@ -184,29 +177,27 @@ const SharedBadgeDisplay = ({ badgeData, type = 'individual' }: { badgeData: Pub
|
|
|
184
177
|
</div>
|
|
185
178
|
|
|
186
179
|
<div className="pt-8">
|
|
187
|
-
<h3 className={
|
|
188
|
-
<div className={
|
|
180
|
+
<h3 className={'text-xl font-bold mb-3'} style={{ color: 'var(--text-main)' }}>2. KYD Technical™ Signals</h3>
|
|
181
|
+
<div className={'prose prose-sm max-w-none mb-6'} style={{ color: 'var(--text-secondary)' }}>
|
|
189
182
|
<p>{developer_trust_explanation}</p>
|
|
190
183
|
</div>
|
|
191
184
|
<ProviderInsights
|
|
192
185
|
platforms={connectedPlatforms || []}
|
|
193
186
|
insights={assessmentResult.provider_insights}
|
|
194
|
-
variant="public"
|
|
195
|
-
view={type}
|
|
196
187
|
/>
|
|
197
188
|
</div>
|
|
198
189
|
|
|
199
190
|
<div className="pt-8">
|
|
200
|
-
<h3 className={
|
|
191
|
+
<h3 className={'text-xl font-bold mb-3'} style={{ color: 'var(--text-main)' }}>3. KYD Risk™ Signals</h3>
|
|
201
192
|
{badgeData.optOutScreening ? (
|
|
202
|
-
<div className={
|
|
193
|
+
<div className={'mb-4 p-4 rounded-lg border'} style={{ backgroundColor: 'var(--icon-button-secondary)', borderColor: 'var(--icon-button-secondary)' }}>
|
|
203
194
|
<div className="flex items-start">
|
|
204
195
|
<span className="h-5 w-5 mr-3 mt-0.5 flex-shrink-0" style={{ color: yellow }}>
|
|
205
196
|
<FiAlertTriangle size={20} />
|
|
206
197
|
</span>
|
|
207
198
|
<div>
|
|
208
|
-
<h4 className={'font-bold'} style={
|
|
209
|
-
<p className={'text-sm mt-1'} style={
|
|
199
|
+
<h4 className={'font-bold'} style={{ color: 'var(--text-main)' }}>User Opted Out of Screening</h4>
|
|
200
|
+
<p className={'text-sm mt-1'} style={{ color: 'var(--text-secondary)' }}>
|
|
210
201
|
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.
|
|
211
202
|
</p>
|
|
212
203
|
</div>
|
|
@@ -214,7 +205,7 @@ const SharedBadgeDisplay = ({ badgeData, type = 'individual' }: { badgeData: Pub
|
|
|
214
205
|
</div>
|
|
215
206
|
) : (
|
|
216
207
|
<>
|
|
217
|
-
<div className={
|
|
208
|
+
<div className={'prose prose-sm max-w-none space-y-4 mb-6'} style={{ color: 'var(--text-secondary)' }}>
|
|
218
209
|
<p>{riskScore?.description || ''}</p>
|
|
219
210
|
</div>
|
|
220
211
|
<IpRiskAnalysisDisplay ipRiskAnalysis={screening_sources?.ip_risk_analysis} />
|
|
@@ -223,8 +214,8 @@ const SharedBadgeDisplay = ({ badgeData, type = 'individual' }: { badgeData: Pub
|
|
|
223
214
|
</div>
|
|
224
215
|
|
|
225
216
|
<div className="pt-8">
|
|
226
|
-
<h3 className={
|
|
227
|
-
<div className={
|
|
217
|
+
<h3 className={'text-xl font-bold mb-3'} style={{ color: 'var(--text-main)' }}>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>
|
|
218
|
+
<div className={'prose prose-sm max-w-none mb-6 space-y-4'} style={{ color: 'var(--text-secondary)' }}>
|
|
228
219
|
<p>{assessmentResult.ai_usage_summary?.explanation}</p>
|
|
229
220
|
{assessmentResult.ai_usage_summary?.key_findings && (
|
|
230
221
|
<ul className="list-disc list-inside">
|
|
@@ -237,41 +228,39 @@ const SharedBadgeDisplay = ({ badgeData, type = 'individual' }: { badgeData: Pub
|
|
|
237
228
|
</div>
|
|
238
229
|
|
|
239
230
|
<div className="pt-8">
|
|
240
|
-
<h3 className={
|
|
241
|
-
<div className={
|
|
231
|
+
<h3 className={'text-xl font-bold mb-3'} style={{ color: 'var(--text-main)' }}>5. Industry Considerations</h3>
|
|
232
|
+
<div className={'prose prose-sm max-w-none'} style={{ color: 'var(--text-secondary)' }}>
|
|
242
233
|
<p>{industry_considerations}</p>
|
|
243
234
|
</div>
|
|
244
235
|
</div>
|
|
245
236
|
|
|
246
237
|
{!badgeData.optOutScreening && screening_sources && (
|
|
247
238
|
<div className="pt-8">
|
|
248
|
-
<h3 className={
|
|
239
|
+
<h3 className={'text-2xl font-bold mb-4'} style={{ color: 'var(--text-main)' }}>6. Appendix: Data Sources</h3>
|
|
249
240
|
<div className="space-y-8">
|
|
250
241
|
<div>
|
|
251
|
-
<h4 className={
|
|
242
|
+
<h4 className={'text-xl font-bold mb-4'} style={{ color: 'var(--text-main)' }}>Sanctions & Watchlists</h4>
|
|
252
243
|
<AppendixTables
|
|
253
244
|
type="sanctions"
|
|
254
245
|
sources={[...(screening_sources.ofac_lists || []), ...(screening_sources.additional_watchlists || [])]}
|
|
255
246
|
searchedAt={updatedAt}
|
|
256
247
|
developerName={developerName || 'this developer'}
|
|
257
|
-
view={type}
|
|
258
248
|
/>
|
|
259
249
|
</div>
|
|
260
250
|
<div>
|
|
261
|
-
<h4 className={
|
|
251
|
+
<h4 className={'text-xl font-bold mb-4'} style={{ color: 'var(--text-main)' }}>Country-specific Entity Affiliations</h4>
|
|
262
252
|
<AppendixTables
|
|
263
253
|
type="domains"
|
|
264
254
|
sources={screening_sources.risk_profile_domains || []}
|
|
265
255
|
searchedAt={updatedAt}
|
|
266
256
|
developerName={developerName || 'this developer'}
|
|
267
|
-
view={type}
|
|
268
257
|
/>
|
|
269
258
|
</div>
|
|
270
259
|
</div>
|
|
271
260
|
</div>
|
|
272
261
|
)}
|
|
273
262
|
|
|
274
|
-
<div className={
|
|
263
|
+
<div className={'pt-8 text-sm text-center'} style={{ color: 'var(--text-secondary)' }}>
|
|
275
264
|
Report Completed: {new Date(updatedAt).toLocaleString(undefined, {
|
|
276
265
|
year: 'numeric',
|
|
277
266
|
month: 'long',
|
|
@@ -283,8 +272,8 @@ const SharedBadgeDisplay = ({ badgeData, type = 'individual' }: { badgeData: Pub
|
|
|
283
272
|
</div>
|
|
284
273
|
</div>
|
|
285
274
|
</div>
|
|
286
|
-
<footer className={
|
|
287
|
-
<p className={
|
|
275
|
+
<footer className={'mt-12 pt-6 border-t'} style={{ borderColor: 'var(--icon-button-secondary)' }}>
|
|
276
|
+
<p className={'text-center text-xs max-w-4xl mx-auto'} style={{ color: 'var(--text-secondary)' }}>
|
|
288
277
|
© 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.
|
|
289
278
|
</p>
|
|
290
279
|
</footer>
|
package/src/colors.ts
CHANGED
|
@@ -2,4 +2,42 @@ const red = '#EC6662'
|
|
|
2
2
|
const yellow = '#ffbb54'
|
|
3
3
|
const green = '#02a389'
|
|
4
4
|
|
|
5
|
-
export { red, yellow, green }
|
|
5
|
+
export { red, yellow, green }
|
|
6
|
+
|
|
7
|
+
// red
|
|
8
|
+
|
|
9
|
+
// #EC6662
|
|
10
|
+
|
|
11
|
+
// #F1908C
|
|
12
|
+
|
|
13
|
+
// #F5B7B3
|
|
14
|
+
|
|
15
|
+
// #FADBDA
|
|
16
|
+
|
|
17
|
+
// #FDF0EF
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
// // yellow
|
|
21
|
+
|
|
22
|
+
// #F4BE66
|
|
23
|
+
|
|
24
|
+
// #F7CF8F
|
|
25
|
+
|
|
26
|
+
// #F9DFB6
|
|
27
|
+
|
|
28
|
+
// #FDEFDB
|
|
29
|
+
|
|
30
|
+
// #FEF9F0
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
// // green
|
|
34
|
+
|
|
35
|
+
// #02a389
|
|
36
|
+
|
|
37
|
+
// #7BB9AA
|
|
38
|
+
|
|
39
|
+
// #A9D1C6
|
|
40
|
+
|
|
41
|
+
// #D5E8E2
|
|
42
|
+
|
|
43
|
+
// #EEF6F4
|
|
@@ -22,54 +22,51 @@ interface AppendixTableProps {
|
|
|
22
22
|
sources: string[] | DomainCSVRow[];
|
|
23
23
|
searchedAt: string;
|
|
24
24
|
developerName: string;
|
|
25
|
-
view?: 'recruiter' | 'individual';
|
|
26
25
|
}
|
|
27
26
|
|
|
28
|
-
const SanctionsRow = ({ source, searchedAt, developerName
|
|
29
|
-
<tr className={
|
|
30
|
-
<td className={
|
|
27
|
+
const SanctionsRow = ({ source, searchedAt, developerName }: { source: SanctionSource, searchedAt: string, developerName: string }) => (
|
|
28
|
+
<tr className={'transition-colors hover:bg-black/5'}>
|
|
29
|
+
<td className={'px-4 py-4 whitespace-nowrap text-sm font-medium'} style={{ color: 'var(--text-main)' }}>
|
|
31
30
|
{source.issuingEntity}
|
|
32
31
|
</td>
|
|
33
|
-
<td className={
|
|
32
|
+
<td className={'px-4 py-4 whitespace-normal text-sm'} style={{ color: 'var(--text-secondary)' }}>
|
|
34
33
|
{source.listName}
|
|
35
34
|
</td>
|
|
36
|
-
<td className={
|
|
35
|
+
<td className={'px-4 py-4 whitespace-nowrap text-sm'} style={{ color: 'var(--text-secondary)' }}>
|
|
37
36
|
{searchedAt}
|
|
38
37
|
</td>
|
|
39
|
-
<td className={
|
|
40
|
-
<span className={
|
|
38
|
+
<td className={'px-4 py-4 whitespace-nowrap text-sm'} style={{ color: 'var(--text-secondary)' }}>
|
|
39
|
+
<span className={'px-2 inline-flex text-xs leading-5 font-semibold rounded-full'} style={{ backgroundColor: 'var(--icon-button-secondary)', color: 'var(--text-main)' }}>
|
|
41
40
|
Not Found
|
|
42
41
|
</span>
|
|
43
42
|
</td>
|
|
44
|
-
<td className={
|
|
45
|
-
No exact match for <strong style={
|
|
43
|
+
<td className={'px-4 py-4 text-sm whitespace-normal'} style={{ color: 'var(--text-secondary)' }}>
|
|
44
|
+
No exact match for <strong style={{ color: 'var(--text-main)' }}>{developerName}</strong> (based on name and email) was found on this list.
|
|
46
45
|
</td>
|
|
47
46
|
</tr>
|
|
48
47
|
);
|
|
49
48
|
|
|
50
|
-
const DomainRow = ({ source, searchedAt, developerName
|
|
51
|
-
<tr className={
|
|
52
|
-
<td className={
|
|
53
|
-
<td className={
|
|
54
|
-
<td className={
|
|
55
|
-
<td className={
|
|
56
|
-
<td className={
|
|
57
|
-
<span className={
|
|
49
|
+
const DomainRow = ({ source, searchedAt, developerName }: { source: DomainSource, searchedAt: string, developerName: string }) => (
|
|
50
|
+
<tr className={'transition-colors hover:bg-black/5'}>
|
|
51
|
+
<td className={'px-4 py-4 whitespace-normal text-sm'} style={{ color: 'var(--text-secondary)' }}>{source.country || 'N/A'}</td>
|
|
52
|
+
<td className={'px-4 py-4 whitespace-normal text-sm'} style={{ color: 'var(--text-secondary)' }}>{source.entityType || 'N/A'}</td>
|
|
53
|
+
<td className={'px-4 py-4 whitespace-normal text-sm font-medium'} style={{ color: 'var(--text-main)' }}>{source.entityName || source.url}</td>
|
|
54
|
+
<td className={'px-4 py-4 whitespace-nowrap text-sm'} style={{ color: 'var(--text-secondary)' }}>{searchedAt}</td>
|
|
55
|
+
<td className={'px-4 py-4 whitespace-nowrap text-sm'} style={{ color: 'var(--text-secondary)' }}>
|
|
56
|
+
<span className={'px-2 inline-flex text-xs leading-5 font-semibold rounded-full'} style={{ backgroundColor: 'var(--icon-button-secondary)', color: 'var(--text-main)' }}>
|
|
58
57
|
Not Found
|
|
59
58
|
</span>
|
|
60
59
|
</td>
|
|
61
|
-
<td className={
|
|
62
|
-
No profile matching <strong style={
|
|
60
|
+
<td className={'px-4 py-4 text-sm whitespace-normal'} style={{ color: 'var(--text-secondary)' }}>
|
|
61
|
+
No profile matching <strong style={{ color: 'var(--text-main)' }}>{developerName}</strong> (based on name and email) was found at this domain.
|
|
63
62
|
</td>
|
|
64
63
|
</tr>
|
|
65
64
|
);
|
|
66
65
|
|
|
67
66
|
|
|
68
|
-
const AppendixTables: React.FC<AppendixTableProps> = ({ type, sources, searchedAt, developerName
|
|
67
|
+
const AppendixTables: React.FC<AppendixTableProps> = ({ type, sources, searchedAt, developerName }) => {
|
|
69
68
|
const [visibleCount, setVisibleCount] = useState(PAGE_SIZE);
|
|
70
69
|
|
|
71
|
-
const isRecruiter = view === 'recruiter';
|
|
72
|
-
|
|
73
70
|
const formattedDate = new Date(searchedAt).toLocaleString(undefined, {
|
|
74
71
|
year: 'numeric', month: 'short', day: 'numeric',
|
|
75
72
|
hour: 'numeric', minute: '2-digit',
|
|
@@ -103,36 +100,36 @@ const AppendixTables: React.FC<AppendixTableProps> = ({ type, sources, searchedA
|
|
|
103
100
|
|
|
104
101
|
return (
|
|
105
102
|
<div>
|
|
106
|
-
<div className={
|
|
107
|
-
<table className={
|
|
108
|
-
<thead className={
|
|
103
|
+
<div className={'overflow-x-auto rounded-lg border'} style={{ borderColor: 'var(--icon-button-secondary)', backgroundColor: 'var(--content-card-background)' }}>
|
|
104
|
+
<table className={'min-w-full'}>
|
|
105
|
+
<thead className={''}>
|
|
109
106
|
<tr>
|
|
110
107
|
{headers.map(header => (
|
|
111
|
-
<th key={header} scope="col" className={
|
|
108
|
+
<th key={header} scope="col" className={'px-4 py-3 text-left text-xs font-semibold uppercase tracking-wider'} style={{ color: 'var(--text-secondary)' }}>
|
|
112
109
|
{header}
|
|
113
110
|
</th>
|
|
114
111
|
))}
|
|
115
112
|
</tr>
|
|
116
113
|
</thead>
|
|
117
|
-
<tbody className={
|
|
114
|
+
<tbody className={''}>
|
|
118
115
|
{visibleParsedSources.map((source, index) =>
|
|
119
116
|
type === 'sanctions'
|
|
120
|
-
? <SanctionsRow key={index} source={source as SanctionSource} searchedAt={formattedDate} developerName={developerName}
|
|
121
|
-
: <DomainRow key={index} source={source as DomainSource} searchedAt={formattedDate} developerName={developerName}
|
|
117
|
+
? <SanctionsRow key={index} source={source as SanctionSource} searchedAt={formattedDate} developerName={developerName} />
|
|
118
|
+
: <DomainRow key={index} source={source as DomainSource} searchedAt={formattedDate} developerName={developerName} />
|
|
122
119
|
)}
|
|
123
120
|
</tbody>
|
|
124
121
|
</table>
|
|
125
122
|
</div>
|
|
126
123
|
{parsedSources.length > PAGE_SIZE && (
|
|
127
|
-
<div className={
|
|
124
|
+
<div className={'mt-4 flex items-center justify-between text-sm'} style={{ color: 'var(--text-secondary)' }}>
|
|
128
125
|
<p>
|
|
129
126
|
Showing {Math.min(visibleCount, parsedSources.length)} of {parsedSources.length} entries
|
|
130
127
|
</p>
|
|
131
128
|
{visibleCount < parsedSources.length && (
|
|
132
129
|
<button
|
|
133
130
|
onClick={handleLoadMore}
|
|
134
|
-
className={
|
|
135
|
-
style={
|
|
131
|
+
className={'font-medium underline-offset-2 hover:underline'}
|
|
132
|
+
style={{ color: 'var(--icon-accent)' }}
|
|
136
133
|
>
|
|
137
134
|
Load More
|
|
138
135
|
</button>
|
|
@@ -8,25 +8,25 @@ interface IpRiskAnalysisDisplayProps {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
const FindingRow = ({ finding }: { finding: IpRiskAnalysisFinding }) => (
|
|
11
|
-
<div className="grid grid-cols-1 md:grid-cols-3 gap-x-8 gap-y-2 py-4 border-b
|
|
11
|
+
<div className="grid grid-cols-1 md:grid-cols-3 gap-x-8 gap-y-2 py-4 border-b" style={{ borderColor: 'var(--icon-button-secondary)' }}>
|
|
12
12
|
<div className="md:col-span-1">
|
|
13
|
-
<p className="font-semibold
|
|
13
|
+
<p className="font-semibold" style={{ color: 'var(--text-main)' }}>{finding.label}</p>
|
|
14
14
|
</div>
|
|
15
15
|
<div className="md:col-span-2">
|
|
16
|
-
<p
|
|
17
|
-
<p className="text-xs
|
|
16
|
+
<p style={{ color: 'var(--text-secondary)' }}>{finding.details}</p>
|
|
17
|
+
<p className="text-xs mt-1 italic" style={{ color: 'var(--text-secondary)' }}>{finding.implication}</p>
|
|
18
18
|
</div>
|
|
19
19
|
</div>
|
|
20
20
|
);
|
|
21
21
|
|
|
22
22
|
const Section = ({ section }: { section: IpRiskSection }) => (
|
|
23
23
|
<div className="py-3">
|
|
24
|
-
<h4 className="text-lg font-semibold mb-2
|
|
24
|
+
<h4 className="text-lg font-semibold mb-2" style={{ color: 'var(--text-main)' }}>{section.title}</h4>
|
|
25
25
|
<div className="space-y-3">
|
|
26
26
|
{section.items.map((item, idx) => (
|
|
27
|
-
<div key={idx}
|
|
28
|
-
<p className="font-medium
|
|
29
|
-
<p className="text-sm
|
|
27
|
+
<div key={idx}>
|
|
28
|
+
<p className="font-medium" style={{ color: 'var(--text-main)' }}>{item.label}:</p>
|
|
29
|
+
<p className="text-sm" style={{ color: 'var(--text-secondary)' }}>{item.text}</p>
|
|
30
30
|
</div>
|
|
31
31
|
))}
|
|
32
32
|
</div>
|
|
@@ -36,9 +36,9 @@ const Section = ({ section }: { section: IpRiskSection }) => (
|
|
|
36
36
|
const IpRiskAnalysisDisplay = ({ ipRiskAnalysis }: IpRiskAnalysisDisplayProps) => {
|
|
37
37
|
if (!ipRiskAnalysis || !ipRiskAnalysis.checked || !ipRiskAnalysis.findings || ipRiskAnalysis.findings.length === 0) {
|
|
38
38
|
return (
|
|
39
|
-
<div className="mt-6 p-4
|
|
40
|
-
<FiInfo className="h-5 w-5
|
|
41
|
-
<p className="text-sm
|
|
39
|
+
<div className="mt-6 p-4 rounded-lg flex items-center" style={{ backgroundColor: 'var(--content-card-background)' }}>
|
|
40
|
+
<FiInfo className="h-5 w-5 mr-3 flex-shrink-0" style={{ color: 'var(--text-secondary)' }} />
|
|
41
|
+
<p className="text-sm" style={{ color: 'var(--text-secondary)' }}>IP risk analysis was not performed for this assessment.</p>
|
|
42
42
|
</div>
|
|
43
43
|
);
|
|
44
44
|
}
|
|
@@ -47,7 +47,7 @@ const IpRiskAnalysisDisplay = ({ ipRiskAnalysis }: IpRiskAnalysisDisplayProps) =
|
|
|
47
47
|
|
|
48
48
|
if (sections) {
|
|
49
49
|
return (
|
|
50
|
-
<div className="mt-
|
|
50
|
+
<div className="mt-2 divide-y" style={{ borderColor: 'var(--icon-button-secondary)' }}>
|
|
51
51
|
<Section section={sections.general} />
|
|
52
52
|
<Section section={sections.location} />
|
|
53
53
|
<Section section={sections.reputational} />
|
|
@@ -57,7 +57,7 @@ const IpRiskAnalysisDisplay = ({ ipRiskAnalysis }: IpRiskAnalysisDisplayProps) =
|
|
|
57
57
|
|
|
58
58
|
return (
|
|
59
59
|
<div className="mt-6">
|
|
60
|
-
<div className="divide-y
|
|
60
|
+
<div className="divide-y" style={{ borderColor: 'var(--icon-button-secondary)' }}>
|
|
61
61
|
{findings.map((finding, index) => (
|
|
62
62
|
<FindingRow key={index} finding={finding} />
|
|
63
63
|
))}
|
|
@@ -13,8 +13,6 @@ interface Platform {
|
|
|
13
13
|
interface ProviderInsightsProps {
|
|
14
14
|
platforms: Platform[];
|
|
15
15
|
insights: { [provider: string]: ProviderInsight };
|
|
16
|
-
variant?: 'public' | 'private';
|
|
17
|
-
view?: 'recruiter' | 'individual';
|
|
18
16
|
}
|
|
19
17
|
|
|
20
18
|
const providerIcons: { [key: string]: IconType } = {
|
|
@@ -29,14 +27,11 @@ const providerIcons: { [key: string]: IconType } = {
|
|
|
29
27
|
'Google Scholar': FaGoogle,
|
|
30
28
|
};
|
|
31
29
|
|
|
32
|
-
export default function ProviderInsights({ platforms, insights
|
|
30
|
+
export default function ProviderInsights({ platforms, insights }: ProviderInsightsProps) {
|
|
33
31
|
if (!platforms || platforms.length === 0) {
|
|
34
32
|
return null;
|
|
35
33
|
}
|
|
36
34
|
|
|
37
|
-
const isPublic = variant === 'public';
|
|
38
|
-
const isRecruiter = view === 'recruiter';
|
|
39
|
-
|
|
40
35
|
return (
|
|
41
36
|
<div className="space-y-6">
|
|
42
37
|
{platforms.map(platform => {
|
|
@@ -51,22 +46,22 @@ export default function ProviderInsights({ platforms, insights, variant = 'priva
|
|
|
51
46
|
return (
|
|
52
47
|
<div
|
|
53
48
|
key={platform.name}
|
|
54
|
-
className={
|
|
55
|
-
style={
|
|
49
|
+
className={'rounded-xl shadow-sm border'}
|
|
50
|
+
style={{ backgroundColor: 'var(--content-card-background)', borderColor: 'var(--icon-button-secondary)' }}
|
|
56
51
|
>
|
|
57
52
|
{/* Header */}
|
|
58
53
|
<div
|
|
59
54
|
className="p-4 border-b"
|
|
60
|
-
style={
|
|
55
|
+
style={{ borderColor: 'var(--icon-button-secondary)' }}
|
|
61
56
|
>
|
|
62
57
|
<div className="flex items-center justify-between">
|
|
63
58
|
<div className="flex items-center space-x-3">
|
|
64
|
-
{Icon && <Icon className={
|
|
65
|
-
<h3 className={
|
|
59
|
+
{Icon && <Icon className={'h-6 w-6'} style={{ color: 'var(--text-secondary)' }} />}
|
|
60
|
+
<h3 className={'font-semibold text-lg'} style={{ color: 'var(--text-main)' }}>{platform.name}</h3>
|
|
66
61
|
</div>
|
|
67
62
|
{observedDate && (
|
|
68
|
-
<span className={
|
|
69
|
-
|
|
63
|
+
<span className={'text-sm'} style={{ color: 'var(--text-secondary)' }}>
|
|
64
|
+
Observed: {observedDate}
|
|
70
65
|
</span>
|
|
71
66
|
)}
|
|
72
67
|
</div>
|
|
@@ -75,8 +70,8 @@ export default function ProviderInsights({ platforms, insights, variant = 'priva
|
|
|
75
70
|
href={platform.url}
|
|
76
71
|
target="_blank"
|
|
77
72
|
rel="noopener noreferrer"
|
|
78
|
-
className={
|
|
79
|
-
style={
|
|
73
|
+
className={'mt-2 inline-block text-sm font-medium underline-offset-2 hover:underline'}
|
|
74
|
+
style={{ color: 'var(--icon-accent)' }}
|
|
80
75
|
>
|
|
81
76
|
{platform.handle || 'View Profile'}
|
|
82
77
|
</a>
|
|
@@ -87,23 +82,23 @@ export default function ProviderInsights({ platforms, insights, variant = 'priva
|
|
|
87
82
|
{providerInsight && (
|
|
88
83
|
<div className="p-4">
|
|
89
84
|
{/* Summary */}
|
|
90
|
-
<p className={
|
|
85
|
+
<p className={'text-sm mb-4'} style={{ color: 'var(--text-secondary)' }}>
|
|
91
86
|
{providerInsight.summary}
|
|
92
87
|
</p>
|
|
93
88
|
|
|
94
89
|
{/* Data Points */}
|
|
95
90
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
96
91
|
{providerInsight.data_points.map((point, index) => (
|
|
97
|
-
<div key={index} className={
|
|
92
|
+
<div key={index} className={'rounded-lg p-4'} style={{ backgroundColor: 'var(--content-card-background)/90' }}>
|
|
98
93
|
<div className="flex justify-between items-baseline mb-2">
|
|
99
|
-
<h4 className={
|
|
94
|
+
<h4 className={'font-medium'} style={{ color: 'var(--text-main)' }}>
|
|
100
95
|
{point.label}
|
|
101
96
|
</h4>
|
|
102
|
-
<span className={
|
|
97
|
+
<span className={'font-bold'} style={{ color: 'var(--text-main)' }}>
|
|
103
98
|
{point.value}
|
|
104
99
|
</span>
|
|
105
100
|
</div>
|
|
106
|
-
<p className={
|
|
101
|
+
<p className={'text-sm'} style={{ color: 'var(--text-secondary)' }}>
|
|
107
102
|
{point.significance}
|
|
108
103
|
</p>
|
|
109
104
|
</div>
|
|
@@ -8,6 +8,20 @@ const getBadgeImageUrl = (score: number) => {
|
|
|
8
8
|
return '/badgered.png';
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
+
const hexToRgba = (hex: string, alpha: number) => {
|
|
12
|
+
const clean = hex.replace('#', '');
|
|
13
|
+
const r = parseInt(clean.substring(0, 2), 16);
|
|
14
|
+
const g = parseInt(clean.substring(2, 4), 16);
|
|
15
|
+
const b = parseInt(clean.substring(4, 6), 16);
|
|
16
|
+
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const pickTint = (score: number) => {
|
|
20
|
+
if (score >= 75) return '#02a389';
|
|
21
|
+
if (score >= 50) return '#ffbb54';
|
|
22
|
+
return '#EC6662';
|
|
23
|
+
};
|
|
24
|
+
|
|
11
25
|
interface ReportHeaderProps {
|
|
12
26
|
badgeId: string | undefined;
|
|
13
27
|
developerName: string | undefined;
|
|
@@ -15,13 +29,12 @@ interface ReportHeaderProps {
|
|
|
15
29
|
score: number | undefined;
|
|
16
30
|
isPublic: boolean;
|
|
17
31
|
badgeImageUrl: string;
|
|
18
|
-
type?: 'recruiter' | 'individual';
|
|
19
32
|
}
|
|
20
33
|
|
|
21
|
-
const ReportHeader = ({ badgeId, developerName, updatedAt, score = 0, isPublic, badgeImageUrl
|
|
34
|
+
const ReportHeader = ({ badgeId, developerName, updatedAt, score = 0, isPublic, badgeImageUrl }: ReportHeaderProps) => {
|
|
22
35
|
// Use the dynamic image if available, otherwise fall back to the score-based one.
|
|
23
36
|
const finalBadgeImageUrl = badgeImageUrl || getBadgeImageUrl(score);
|
|
24
|
-
const
|
|
37
|
+
const tint = hexToRgba(pickTint(score), 0.06);
|
|
25
38
|
|
|
26
39
|
const formattedDate = updatedAt ? new Date(updatedAt).toLocaleString(undefined, {
|
|
27
40
|
year: 'numeric',
|
|
@@ -31,38 +44,34 @@ const ReportHeader = ({ badgeId, developerName, updatedAt, score = 0, isPublic,
|
|
|
31
44
|
|
|
32
45
|
return (
|
|
33
46
|
<div
|
|
34
|
-
className={
|
|
35
|
-
|
|
36
|
-
? 'mb-8 p-6 rounded-xl shadow-lg flex flex-col md:flex-row items-start md:items-center justify-between gap-6 border'
|
|
37
|
-
: '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'
|
|
38
|
-
}
|
|
39
|
-
style={isRecruiter ? { backgroundColor: 'var(--content-card-background)', borderColor: 'var(--icon-button-secondary)' } : undefined}
|
|
47
|
+
className={'mb-8 p-6 rounded-xl shadow-lg flex flex-col md:flex-row items-start md:items-center justify-between gap-6 border'}
|
|
48
|
+
style={{ backgroundColor: 'var(--content-card-background)', borderColor: 'var(--icon-button-secondary)', backgroundImage: `linear-gradient(${tint}, ${tint})` }}
|
|
40
49
|
>
|
|
41
50
|
{/* Left Section */}
|
|
42
51
|
<div className="flex items-center text-left md:text-center gap-5">
|
|
43
52
|
<Image src={finalBadgeImageUrl} alt="KYD Badge" width={100} height={100} unoptimized />
|
|
44
53
|
<div className='flex flex-col'>
|
|
45
|
-
<h1 className={
|
|
54
|
+
<h1 className={'font-bold text-lg'} style={{ color: 'var(--text-main)' }}>
|
|
46
55
|
KYD Self-Check™
|
|
47
56
|
</h1>
|
|
48
|
-
<p className={
|
|
49
|
-
|
|
57
|
+
<p className={'text-sm'} style={{ color: 'var(--text-secondary)' }}>
|
|
58
|
+
Private Report
|
|
50
59
|
</p>
|
|
51
60
|
</div>
|
|
52
61
|
</div>
|
|
53
62
|
|
|
54
63
|
{/* Middle Section */}
|
|
55
64
|
<div className="text-left md:text-center">
|
|
56
|
-
<p className={
|
|
57
|
-
<p className={
|
|
65
|
+
<p className={'text-sm'} style={{ color: 'var(--text-secondary)' }}>Developer</p>
|
|
66
|
+
<p className={'font-semibold text-2xl'} style={{ color: 'var(--text-main)' }}>{developerName || 'N/A'}</p>
|
|
58
67
|
</div>
|
|
59
68
|
|
|
60
69
|
{/* Right Section */}
|
|
61
|
-
<div className={
|
|
62
|
-
<p><span className={
|
|
63
|
-
<p><span className={
|
|
64
|
-
<p><span className={
|
|
65
|
-
<p><span className={
|
|
70
|
+
<div className={'text-left text-sm space-y-1'} style={{ color: 'var(--text-secondary)' }}>
|
|
71
|
+
<p><span className={'font-semibold'} style={{ color: 'var(--text-main)' }}>Requested By:</span> {developerName || 'N/A'}</p>
|
|
72
|
+
<p><span className={'font-semibold'} style={{ color: 'var(--text-main)' }}>Organization:</span> Unaffiliated</p>
|
|
73
|
+
<p><span className={'font-semibold'} style={{ color: 'var(--text-main)' }}>Date Generated:</span> {formattedDate}</p>
|
|
74
|
+
<p><span className={'font-semibold'} style={{ color: 'var(--text-main)' }}>Report ID:</span> {badgeId}</p>
|
|
66
75
|
</div>
|
|
67
76
|
</div>
|
|
68
77
|
);
|
|
@@ -127,7 +127,7 @@ const ShareButton = ({ badgeId, shareTitle, shareText, buttonText, className, is
|
|
|
127
127
|
<button
|
|
128
128
|
onClick={handleButtonClick}
|
|
129
129
|
disabled={!badgeId || !url || isToggling}
|
|
130
|
-
className={className || "flex items-center px-4 py-2
|
|
130
|
+
className={className || "flex items-center px-4 py-2 text-white rounded-md transition-colors disabled:opacity-50 disabled:cursor-not-allowed bg-[var(--icon-accent)] hover:bg-[var(--icon-accent-hover)]"}
|
|
131
131
|
>
|
|
132
132
|
{isToggling ? (
|
|
133
133
|
<FiRefreshCw className="animate-spin" />
|
|
@@ -138,7 +138,7 @@ const ShareButton = ({ badgeId, shareTitle, shareText, buttonText, className, is
|
|
|
138
138
|
</button>
|
|
139
139
|
|
|
140
140
|
{isMenuOpen && (
|
|
141
|
-
<div className="absolute right-0 mt-2 w-56 origin-top-right
|
|
141
|
+
<div className="absolute right-0 mt-2 w-56 origin-top-right rounded-md shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none z-20" style={{ backgroundColor: 'var(--content-card-background)' }}>
|
|
142
142
|
<div className="py-1">
|
|
143
143
|
{socialPlatforms.map((platform) => {
|
|
144
144
|
const Icon = platform.icon;
|
|
@@ -149,27 +149,30 @@ const ShareButton = ({ badgeId, shareTitle, shareText, buttonText, className, is
|
|
|
149
149
|
target="_blank"
|
|
150
150
|
rel="noopener noreferrer"
|
|
151
151
|
onClick={() => setIsMenuOpen(false)}
|
|
152
|
-
className="flex items-center w-full px-4 py-2 text-sm
|
|
152
|
+
className="flex items-center w-full px-4 py-2 text-sm hover:bg-black/5"
|
|
153
|
+
style={{ color: 'var(--text-main)' }}
|
|
153
154
|
>
|
|
154
155
|
<Icon className={`mr-3 h-5 w-5 ${platform.color}`} />
|
|
155
156
|
<span>Share on {platform.name}</span>
|
|
156
157
|
</a>
|
|
157
158
|
);
|
|
158
159
|
})}
|
|
159
|
-
<div className="
|
|
160
|
+
<div className="my-1" style={{ borderTop: '1px solid var(--icon-button-secondary)' }}></div>
|
|
160
161
|
<button
|
|
161
162
|
onClick={handleCopyLink}
|
|
162
|
-
className="flex items-center w-full px-4 py-2 text-sm
|
|
163
|
+
className="flex items-center w-full px-4 py-2 text-sm hover:bg-black/5"
|
|
164
|
+
style={{ color: 'var(--text-main)' }}
|
|
163
165
|
>
|
|
164
166
|
<FiLink className="mr-3 h-5 w-5" />
|
|
165
167
|
<span>Copy Link</span>
|
|
166
168
|
</button>
|
|
167
169
|
{isNativeShareSupported && (
|
|
168
170
|
<>
|
|
169
|
-
<div className="
|
|
171
|
+
<div className="my-1" style={{ borderTop: '1px solid var(--icon-button-secondary)' }}></div>
|
|
170
172
|
<button
|
|
171
173
|
onClick={handleNativeShare}
|
|
172
|
-
className="flex items-center w-full px-4 py-2 text-sm
|
|
174
|
+
className="flex items-center w-full px-4 py-2 text-sm hover:bg-black/5"
|
|
175
|
+
style={{ color: 'var(--text-main)' }}
|
|
173
176
|
>
|
|
174
177
|
<FiShare2 className="mr-3 h-5 w-5" />
|
|
175
178
|
<span>Share via...</span>
|