kyd-shared-badge 0.2.22 → 0.2.24

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kyd-shared-badge",
3
- "version": "0.2.22",
3
+ "version": "0.2.24",
4
4
  "private": false,
5
5
  "main": "./src/index.ts",
6
6
  "module": "./src/index.ts",
@@ -57,48 +57,8 @@ const getAiIconSrc = (descriptor?: string): string => {
57
57
  return '/badge/aired.png';
58
58
  };
59
59
 
60
- interface ScoreCardProps {
61
- title: React.ReactNode;
62
- score: number;
63
- description: string;
64
- descriptor?: string;
65
- scoreType: 'number' | 'risk' | 'descriptor';
66
- iconImageSrc: string;
67
- }
68
-
69
- const ScoreCard = ({ title, score, description, descriptor, scoreType, iconImageSrc }: ScoreCardProps) => {
70
- const isDescriptor = scoreType === 'descriptor';
71
- const scoreHex = getScoreColor(score);
72
- const overlay = scoreHex ? hexToRgba(scoreHex, 0.08) : 'transparent';
73
- let displayScore;
74
-
75
- if (scoreType === 'risk') {
76
- displayScore = getRiskText(score);
77
- } else if (scoreType === 'descriptor') {
78
- displayScore = descriptor;
79
- } else {
80
- displayScore = `${score}/100`;
81
- }
82
-
83
- return (
84
- <div
85
- className={
86
- 'p-6 rounded-xl flex flex-col items-center justify-start text-center shadow h-full border'
87
- }
88
- style={{ backgroundColor: 'var(--content-card-background)', borderColor: 'var(--icon-button-secondary)', backgroundImage: `linear-gradient(${overlay}, ${overlay})` }}
89
- >
90
- <div className={`text-3xl mb-4`} style={scoreHex ? { color: scoreHex } : undefined}>
91
- <Image src={iconImageSrc} alt="icon" width={32} height={32} className="h-8 w-8 object-contain" />
92
- </div>
93
- <h3 className={`font-semibold text-xl ${isDescriptor ? 'text-neutral-400' : ''}`} style={{ color: 'var(--text-main)' }}>{title}</h3>
94
- <p className={`text-4xl font-bold`} style={scoreHex ? { color: scoreHex } : undefined}>{displayScore}</p>
95
- <p className={'text-sm text-neutral-600 dark:text-neutral-400 mt-4'} style={{ color: 'var(--text-secondary)' }}>{description}</p>
96
- </div>
97
- );
98
- };
99
-
100
60
  const SharedBadgeDisplay = ({ badgeData }: { badgeData: PublicBadgeData }) => {
101
- const { badgeId, developerName, assessmentResult, updatedAt, connectedPlatforms } = badgeData;
61
+ const { badgeId, developerName, assessmentResult, updatedAt, connectedAccounts } = badgeData;
102
62
  const { summary_scores, report_summary, developer_trust_explanation, key_skills, screening_sources, industry_considerations } = assessmentResult;
103
63
 
104
64
  const devTrustScore = summary_scores.developer_trust;
@@ -109,249 +69,218 @@ const SharedBadgeDisplay = ({ badgeData }: { badgeData: PublicBadgeData }) => {
109
69
 
110
70
  return (
111
71
  <div className={`${wrapperMaxWidth} mx-auto`}>
112
- {/* Share controls removed; app-level pages render their own actions */}
113
- <ReportHeader
114
- badgeId={badgeId}
115
- developerName={badgeData.developerName}
116
- updatedAt={updatedAt}
117
- score={summary_scores.kyd_self_check.score}
118
- isPublic={true}
119
- badgeImageUrl={badgeData.badgeImageUrl || ''}
120
- />
121
- <div
122
- className={'rounded-xl shadow-xl p-6 sm:p-8 mt-8 border'}
123
- style={{ backgroundColor: 'var(--content-card-background)', borderColor: 'var(--icon-button-secondary)' }}
124
- >
125
- <div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-12">
126
- <ScoreCard
127
- title="KYD Technical™"
128
- score={devTrustScore?.score || 0}
129
- description={devTrustScore?.description || ''}
130
- scoreType='number'
131
- iconImageSrc={getTechnicalIconSrc(devTrustScore?.score || 0)}
132
- />
133
- <ScoreCard
134
- title="KYD Risk™"
135
- score={riskScore?.score || 0}
136
- description={riskScore?.description || ''}
137
- scoreType='risk'
138
- iconImageSrc={getRiskIconSrc(riskScore?.score || 0)}
139
- />
140
- <ScoreCard
141
- 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>}
142
- score={aiUsageScore?.score || 0}
143
- description={aiUsageScore?.description || ''}
144
- descriptor={aiUsageScore?.descriptor}
145
- scoreType='descriptor'
146
- iconImageSrc={getAiIconSrc(aiUsageScore?.descriptor)}
147
- />
148
- </div>
72
+ {/* Share controls removed; app-level pages render their own actions */}
73
+ <ReportHeader
74
+ badgeId={badgeId}
75
+ developerName={badgeData.developerName}
76
+ updatedAt={updatedAt}
77
+ score={summary_scores.kyd_self_check.score}
78
+ isPublic={true}
79
+ badgeImageUrl={badgeData.badgeImageUrl || ''}
80
+ />
81
+ <div
82
+ className={'rounded-xl shadow-xl p-6 sm:p-8 mt-8 border'}
83
+ style={{ backgroundColor: 'var(--content-card-background)', borderColor: 'var(--icon-button-secondary)' }}
84
+ >
149
85
 
150
- <div className={'space-y-12 divide-y'} style={{ borderColor: 'var(--icon-button-secondary)' }}>
151
- <div className="pt-8 first:pt-0">
152
- <h3 className={'text-2xl font-bold mb-4'} style={{ color: 'var(--text-main)' }}>1. Summary Findings</h3>
153
- <div className={'prose prose-sm max-w-none'} style={{ color: 'var(--text-secondary)' }}>
154
- <p>{report_summary}</p>
155
- </div>
156
- {key_skills && key_skills.length > 0 && (
157
- <div className="mt-6">
158
- <h4 className={'text-lg font-semibold mb-3'} style={{ color: 'var(--text-main)' }}>Key Skills Observed</h4>
159
- <div className="flex flex-wrap gap-2">
160
- {key_skills.map((skill: string, index: number) => (
161
- <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)' }}>
162
- {skill}
163
- </span>
164
- ))}
86
+ <div className={'space-y-12 divide-y'} style={{ borderColor: 'var(--icon-button-secondary)' }}>
87
+ <div className="pt-8 first:pt-0">
88
+ <h3 className={'text-2xl font-bold mb-4'} style={{ color: 'var(--text-main)' }}>1. Summary Findings</h3>
89
+ <div className={'prose prose-sm max-w-none'} style={{ color: 'var(--text-secondary)' }}>
90
+ <p>{report_summary}</p>
91
+ </div>
92
+ {/* {key_skills && key_skills.length > 0 && (
93
+ <div className="mt-6">
94
+ <h4 className={'text-lg font-semibold mb-3'} style={{ color: 'var(--text-main)' }}>Key Skills Observed</h4>
95
+ <div className="flex flex-wrap gap-2">
96
+ {key_skills.map((skill: string, index: number) => (
97
+ <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)' }}>
98
+ {skill}
99
+ </span>
100
+ ))}
101
+ </div>
102
+ </div>
103
+ )} */}
104
+ </div>
105
+
106
+ <div className="pt-8">
107
+ <h3 className={'text-xl font-bold mb-3'} style={{ color: 'var(--text-main)' }}>2. KYD Technical™ Signals</h3>
108
+ <div className={'prose prose-sm max-w-none mb-6'} style={{ color: 'var(--text-secondary)' }}>
109
+ <p>{developer_trust_explanation}</p>
110
+ </div>
111
+ <ProviderInsights
112
+ platforms={connectedAccounts || []}
113
+ insights={assessmentResult.provider_insights}
114
+ />
115
+ </div>
116
+
117
+ <div className="pt-8">
118
+ <h3 className={'text-xl font-bold mb-3'} style={{ color: 'var(--text-main)' }}>3. KYD Risk™ Signals</h3>
119
+ {badgeData.optOutScreening ? (
120
+ <div className={'mb-4 p-4 rounded-lg border'} style={{ backgroundColor: 'var(--icon-button-secondary)', borderColor: 'var(--icon-button-secondary)' }}>
121
+ <div className="flex items-start">
122
+ <span className="h-5 w-5 mr-3 mt-0.5 flex-shrink-0" style={{ color: yellow }}>
123
+ <FiAlertTriangle size={20} />
124
+ </span>
125
+ <div>
126
+ <h4 className={'font-bold'} style={{ color: 'var(--text-main)' }}>User Opted Out of Screening</h4>
127
+ <p className={'text-sm mt-1'} style={{ color: 'var(--text-secondary)' }}>
128
+ 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.
129
+ </p>
130
+ </div>
165
131
  </div>
166
132
  </div>
167
- )}
168
- </div>
169
-
170
- <div className="pt-8">
171
- <h3 className={'text-xl font-bold mb-3'} style={{ color: 'var(--text-main)' }}>2. KYD Technical™ Signals</h3>
172
- <div className={'prose prose-sm max-w-none mb-6'} style={{ color: 'var(--text-secondary)' }}>
173
- <p>{developer_trust_explanation}</p>
174
- </div>
175
- <ProviderInsights
176
- platforms={connectedPlatforms || []}
177
- insights={assessmentResult.provider_insights}
178
- />
179
- </div>
180
-
181
- <div className="pt-8">
182
- <h3 className={'text-xl font-bold mb-3'} style={{ color: 'var(--text-main)' }}>3. KYD Risk™ Signals</h3>
183
- {badgeData.optOutScreening ? (
184
- <div className={'mb-4 p-4 rounded-lg border'} style={{ backgroundColor: 'var(--icon-button-secondary)', borderColor: 'var(--icon-button-secondary)' }}>
185
- <div className="flex items-start">
186
- <span className="h-5 w-5 mr-3 mt-0.5 flex-shrink-0" style={{ color: yellow }}>
187
- <FiAlertTriangle size={20} />
188
- </span>
133
+ ) : (
134
+ <>
135
+ <div className={'prose prose-sm max-w-none space-y-4 mb-6'} style={{ color: 'var(--text-secondary)' }}>
136
+ <p>{riskScore?.description || ''}</p>
137
+ </div>
138
+ {(() => {
139
+ const ss = assessmentResult?.screening_sources;
140
+ const ofacMatches = ss?.ofac_screen?.matches && (ss.ofac_screen.matches.length > 0);
141
+ const cslDetails = ss?.csl_details && (Array.isArray(ss.csl_details) ? ss.csl_details.length > 0 : Object.keys(ss.csl_details).length > 0);
142
+ const fbiMatches = ss?.fbi_matches && (ss.fbi_matches.length > 0);
143
+ if (!(ofacMatches || cslDetails || fbiMatches)) return null;
144
+ return (
145
+ <div className={'mb-8 rounded-lg border p-4'} style={{ borderColor: 'var(--icon-button-secondary)', backgroundColor: 'var(--content-card-background)' }}>
146
+ <h4 className={'text-lg font-semibold mb-3'} style={{ color: 'var(--text-main)' }}>3A. Sanctions Matches</h4>
147
+ {/* OFAC matches */}
148
+ {ofacMatches && (
149
+ <div className={'mb-4'}>
150
+ <h5 className={'font-semibold mb-2'} style={{ color: 'var(--text-main)' }}>OFAC API Matches</h5>
189
151
  <div>
190
- <h4 className={'font-bold'} style={{ color: 'var(--text-main)' }}>User Opted Out of Screening</h4>
191
- <p className={'text-sm mt-1'} style={{ color: 'var(--text-secondary)' }}>
192
- 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.
193
- </p>
194
- </div>
195
- </div>
196
- </div>
197
- ) : (
198
- <>
199
- <div className={'prose prose-sm max-w-none space-y-4 mb-6'} style={{ color: 'var(--text-secondary)' }}>
200
- <p>{riskScore?.description || ''}</p>
201
- </div>
202
- {(() => {
203
- const ss = assessmentResult?.screening_sources;
204
- const ofacMatches = ss?.ofac_screen?.matches && (ss.ofac_screen.matches.length > 0);
205
- const cslDetails = ss?.csl_details && (Array.isArray(ss.csl_details) ? ss.csl_details.length > 0 : Object.keys(ss.csl_details).length > 0);
206
- const fbiMatches = ss?.fbi_matches && (ss.fbi_matches.length > 0);
207
- if (!(ofacMatches || cslDetails || fbiMatches)) return null;
208
- return (
209
- <div className={'mb-8 rounded-lg border p-4'} style={{ borderColor: 'var(--icon-button-secondary)', backgroundColor: 'var(--content-card-background)' }}>
210
- <h4 className={'text-lg font-semibold mb-3'} style={{ color: 'var(--text-main)' }}>3A. Sanctions Matches</h4>
211
- {/* OFAC matches */}
212
- {ofacMatches && (
213
- <div className={'mb-4'}>
214
- <h5 className={'font-semibold mb-2'} style={{ color: 'var(--text-main)' }}>OFAC API Matches</h5>
215
- <div>
216
- {ss!.ofac_screen!.matches!.map((m: any, i: number) => {
217
- const s = m?.sanction || {};
218
- const title = s.name || 'Unknown';
219
- const programs = (s.programs && s.programs.length) ? ` — Programs: ${s.programs.join(', ')}` : '';
220
- return (
221
- <div key={i} style={{ display: 'grid', gridTemplateColumns: '12px 1fr', columnGap: 8, alignItems: 'start', marginBottom: 6 }}>
222
- <div aria-hidden="true" style={{ width: 6, height: 6, borderRadius: '50%', marginTop: 6, backgroundColor: 'var(--text-secondary)' }} />
223
- <div className={'text-sm'} style={{ color: 'var(--text-secondary)' }}>
224
- <span className={'font-medium'} style={{ color: 'var(--text-main)' }}>{title}</span>
225
- <span>{programs}</span>
226
- {s.entityLink ? (
227
- <>
228
- {' '}— <a href={s.entityLink} target='_blank' rel='noopener noreferrer' className={'underline'} style={{ color: 'var(--icon-accent)' }}>Details</a>
229
- </>
230
- ) : null}
231
- </div>
232
- </div>
233
- );
234
- })}
235
- </div>
236
- <details className={'mt-2'}>
237
- <summary className={'cursor-pointer text-sm font-semibold'} style={{ color: 'var(--text-main)' }}>View OFAC Raw JSON</summary>
238
- <pre className={'mt-2 overflow-auto text-xs p-3 rounded'} style={{ background: 'rgba(0,0,0,0.04)', color: 'var(--text-main)' }}>{JSON.stringify(ss!.ofac_screen!.raw, null, 2)}</pre>
239
- </details>
240
- </div>
241
- )}
242
- {/* CSL details */}
243
- {cslDetails && (
244
- <div className={'mb-4'}>
245
- <h5 className={'font-semibold mb-2'} style={{ color: 'var(--text-main)' }}>U.S. CSL Details</h5>
246
- <details>
247
- <summary className={'cursor-pointer text-sm font-semibold'} style={{ color: 'var(--text-main)' }}>View CSL Raw JSON</summary>
248
- <pre className={'mt-2 overflow-auto text-xs p-3 rounded'} style={{ background: 'rgba(0,0,0,0.04)', color: 'var(--text-main)' }}>{JSON.stringify(ss!.csl_details, null, 2)}</pre>
249
- </details>
250
- </div>
251
- )}
252
- {/* FBI matches */}
253
- {fbiMatches && (
254
- <div className={'mb-2'}>
255
- <h5 className={'font-semibold mb-2'} style={{ color: 'var(--text-main)' }}>FBI Wanted List Matches</h5>
256
- <div>
257
- {ss!.fbi_matches!.map((f: any, i: number) => (
152
+ {ss!.ofac_screen!.matches!.map((m: any, i: number) => {
153
+ const s = m?.sanction || {};
154
+ const title = s.name || 'Unknown';
155
+ const programs = (s.programs && s.programs.length) ? ` — Programs: ${s.programs.join(', ')}` : '';
156
+ return (
258
157
  <div key={i} style={{ display: 'grid', gridTemplateColumns: '12px 1fr', columnGap: 8, alignItems: 'start', marginBottom: 6 }}>
259
158
  <div aria-hidden="true" style={{ width: 6, height: 6, borderRadius: '50%', marginTop: 6, backgroundColor: 'var(--text-secondary)' }} />
260
159
  <div className={'text-sm'} style={{ color: 'var(--text-secondary)' }}>
261
- <span className={'font-medium'} style={{ color: 'var(--text-main)' }}>{f.title || 'Match'}</span>
262
- {f.url ? <> — <a href={f.url} target='_blank' rel='noopener noreferrer' className={'underline'} style={{ color: 'var(--icon-accent)' }}>Details</a></> : null}
160
+ <span className={'font-medium'} style={{ color: 'var(--text-main)' }}>{title}</span>
161
+ <span>{programs}</span>
162
+ {s.entityLink ? (
163
+ <>
164
+ {' '}— <a href={s.entityLink} target='_blank' rel='noopener noreferrer' className={'underline'} style={{ color: 'var(--icon-accent)' }}>Details</a>
165
+ </>
166
+ ) : null}
263
167
  </div>
264
168
  </div>
265
- ))}
266
- </div>
169
+ );
170
+ })}
171
+ </div>
172
+ <details className={'mt-2'}>
173
+ <summary className={'cursor-pointer text-sm font-semibold'} style={{ color: 'var(--text-main)' }}>View OFAC Raw JSON</summary>
174
+ <pre className={'mt-2 overflow-auto text-xs p-3 rounded'} style={{ background: 'rgba(0,0,0,0.04)', color: 'var(--text-main)' }}>{JSON.stringify(ss!.ofac_screen!.raw, null, 2)}</pre>
175
+ </details>
176
+ </div>
177
+ )}
178
+ {/* CSL details */}
179
+ {cslDetails && (
180
+ <div className={'mb-4'}>
181
+ <h5 className={'font-semibold mb-2'} style={{ color: 'var(--text-main)' }}>U.S. CSL Details</h5>
182
+ <details>
183
+ <summary className={'cursor-pointer text-sm font-semibold'} style={{ color: 'var(--text-main)' }}>View CSL Raw JSON</summary>
184
+ <pre className={'mt-2 overflow-auto text-xs p-3 rounded'} style={{ background: 'rgba(0,0,0,0.04)', color: 'var(--text-main)' }}>{JSON.stringify(ss!.csl_details, null, 2)}</pre>
185
+ </details>
186
+ </div>
187
+ )}
188
+ {/* FBI matches */}
189
+ {fbiMatches && (
190
+ <div className={'mb-2'}>
191
+ <h5 className={'font-semibold mb-2'} style={{ color: 'var(--text-main)' }}>FBI Wanted List Matches</h5>
192
+ <div>
193
+ {ss!.fbi_matches!.map((f: any, i: number) => (
194
+ <div key={i} style={{ display: 'grid', gridTemplateColumns: '12px 1fr', columnGap: 8, alignItems: 'start', marginBottom: 6 }}>
195
+ <div aria-hidden="true" style={{ width: 6, height: 6, borderRadius: '50%', marginTop: 6, backgroundColor: 'var(--text-secondary)' }} />
196
+ <div className={'text-sm'} style={{ color: 'var(--text-secondary)' }}>
197
+ <span className={'font-medium'} style={{ color: 'var(--text-main)' }}>{f.title || 'Match'}</span>
198
+ {f.url ? <> — <a href={f.url} target='_blank' rel='noopener noreferrer' className={'underline'} style={{ color: 'var(--icon-accent)' }}>Details</a></> : null}
199
+ </div>
200
+ </div>
201
+ ))}
267
202
  </div>
268
- )}
269
- </div>
270
- );
271
- })()}
272
- <IpRiskAnalysisDisplay ipRiskAnalysis={screening_sources?.ip_risk_analysis} />
273
- </>
203
+ </div>
204
+ )}
205
+ </div>
206
+ );
207
+ })()}
208
+ <IpRiskAnalysisDisplay ipRiskAnalysis={screening_sources?.ip_risk_analysis} />
209
+ </>
210
+ )}
211
+ </div>
212
+
213
+ <div className="pt-8">
214
+ <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>
215
+ <div className={'prose prose-sm max-w-none mb-6 space-y-4'} style={{ color: 'var(--text-secondary)' }}>
216
+ <p>{assessmentResult.ai_usage_summary?.explanation}</p>
217
+ {assessmentResult.ai_usage_summary?.key_findings && (
218
+ <ul className="list-disc list-inside">
219
+ {assessmentResult.ai_usage_summary.key_findings.map((finding, index) => (
220
+ <li key={index}>{finding}</li>
221
+ ))}
222
+ </ul>
274
223
  )}
275
224
  </div>
225
+ </div>
276
226
 
277
- <div className="pt-8">
278
- <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>
279
- <div className={'prose prose-sm max-w-none mb-6 space-y-4'} style={{ color: 'var(--text-secondary)' }}>
280
- <p>{assessmentResult.ai_usage_summary?.explanation}</p>
281
- {assessmentResult.ai_usage_summary?.key_findings && (
282
- <ul className="list-disc list-inside">
283
- {assessmentResult.ai_usage_summary.key_findings.map((finding, index) => (
284
- <li key={index}>{finding}</li>
285
- ))}
286
- </ul>
287
- )}
227
+ {!badgeData.optOutScreening && screening_sources && (
228
+ <div className="pt-8">
229
+ <h3 className={'text-2xl font-bold mb-4'} style={{ color: 'var(--text-main)' }}>6. Appendix: Data Sources</h3>
230
+ <div className="space-y-8">
231
+ <div>
232
+ <h4 className={'text-xl font-bold mb-4'} style={{ color: 'var(--text-main)' }}>Sanctions & Watchlists</h4>
233
+ {(() => {
234
+ const ofacProvided = screening_sources.ofac_screen?.sources || [];
235
+ const lists = [...(screening_sources.ofac_lists || []), ...ofacProvided];
236
+ const seen: { [k: string]: boolean } = {};
237
+ const dedup: string[] = [];
238
+ for (let i = 0; i < lists.length; i++) {
239
+ const val = lists[i];
240
+ if (!seen[val]) { seen[val] = true; dedup.push(val); }
241
+ }
242
+ const detailed = screening_sources.sanctions_sources_detailed || [];
243
+ const useDetailed = detailed && detailed.length > 0;
244
+ return (
245
+ <AppendixTables
246
+ type="sanctions"
247
+ sources={useDetailed ? detailed : dedup}
248
+ searchedAt={updatedAt}
249
+ developerName={developerName || 'this developer'}
250
+ />
251
+ );
252
+ })()}
253
+ </div>
254
+ <div>
255
+ <h4 className={'text-xl font-bold mb-4'} style={{ color: 'var(--text-main)' }}>Country-specific Entity Affiliations</h4>
256
+ <AppendixTables
257
+ type="domains"
258
+ sources={screening_sources.risk_profile_domains || []}
259
+ searchedAt={updatedAt}
260
+ developerName={developerName || 'this developer'}
261
+ />
262
+ </div>
263
+ </div>
288
264
  </div>
289
- </div>
265
+ )}
290
266
 
291
- <div className="pt-8">
292
- <h3 className={'text-xl font-bold mb-3'} style={{ color: 'var(--text-main)' }}>5. Industry Considerations</h3>
293
- <div className={'prose prose-sm max-w-none'} style={{ color: 'var(--text-secondary)' }}>
294
- <p>{industry_considerations}</p>
295
- </div>
296
- </div>
297
-
298
- {!badgeData.optOutScreening && screening_sources && (
299
- <div className="pt-8">
300
- <h3 className={'text-2xl font-bold mb-4'} style={{ color: 'var(--text-main)' }}>6. Appendix: Data Sources</h3>
301
- <div className="space-y-8">
302
- <div>
303
- <h4 className={'text-xl font-bold mb-4'} style={{ color: 'var(--text-main)' }}>Sanctions & Watchlists</h4>
304
- {(() => {
305
- const ofacProvided = screening_sources.ofac_screen?.sources || [];
306
- const lists = [...(screening_sources.ofac_lists || []), ...ofacProvided];
307
- const seen: { [k: string]: boolean } = {};
308
- const dedup: string[] = [];
309
- for (let i = 0; i < lists.length; i++) {
310
- const val = lists[i];
311
- if (!seen[val]) { seen[val] = true; dedup.push(val); }
312
- }
313
- const detailed = screening_sources.sanctions_sources_detailed || [];
314
- const useDetailed = detailed && detailed.length > 0;
315
- return (
316
- <AppendixTables
317
- type="sanctions"
318
- sources={useDetailed ? detailed : dedup}
319
- searchedAt={updatedAt}
320
- developerName={developerName || 'this developer'}
321
- />
322
- );
323
- })()}
324
- </div>
325
- <div>
326
- <h4 className={'text-xl font-bold mb-4'} style={{ color: 'var(--text-main)' }}>Country-specific Entity Affiliations</h4>
327
- <AppendixTables
328
- type="domains"
329
- sources={screening_sources.risk_profile_domains || []}
330
- searchedAt={updatedAt}
331
- developerName={developerName || 'this developer'}
332
- />
333
- </div>
334
- </div>
335
- </div>
336
- )}
337
-
338
- <div className={'pt-8 text-sm text-center'} style={{ color: 'var(--text-secondary)' }}>
339
- Report Completed: {new Date(updatedAt).toLocaleString(undefined, {
340
- year: 'numeric',
341
- month: 'long',
342
- day: 'numeric',
343
- hour: 'numeric',
344
- minute: '2-digit',
345
- timeZoneName: 'short',
346
- })}
347
- </div>
348
- </div>
349
- </div>
350
- <footer className={'mt-12 pt-6 border-t'} style={{ borderColor: 'var(--icon-button-secondary)' }}>
351
- <p className={'text-center text-xs max-w-4xl mx-auto'} style={{ color: 'var(--text-secondary)' }}>
352
- © 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.
353
- </p>
354
- </footer>
267
+ <div className={'pt-8 text-sm text-center'} style={{ color: 'var(--text-secondary)' }}>
268
+ Report Completed: {new Date(updatedAt).toLocaleString(undefined, {
269
+ year: 'numeric',
270
+ month: 'long',
271
+ day: 'numeric',
272
+ hour: 'numeric',
273
+ minute: '2-digit',
274
+ timeZoneName: 'short',
275
+ })}
276
+ </div>
277
+ </div>
278
+ </div>
279
+ <footer className={'mt-12 pt-6 border-t'} style={{ borderColor: 'var(--icon-button-secondary)' }}>
280
+ <p className={'text-center text-xs max-w-4xl mx-auto'} style={{ color: 'var(--text-secondary)' }}>
281
+ © 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.
282
+ </p>
283
+ </footer>
355
284
  </div>
356
285
  );
357
286
  };
@@ -35,8 +35,9 @@ export default function ProviderInsights({ platforms, insights }: ProviderInsigh
35
35
  return (
36
36
  <div className="space-y-6">
37
37
  {platforms.map(platform => {
38
- const Icon = platform.name ? providerIcons[platform.name] : null;
39
- const providerInsight = insights[platform.name];
38
+ const normalizedName = platform.name || '';
39
+ const Icon = normalizedName ? (providerIcons[normalizedName] || null) : null;
40
+ const providerInsight = insights[normalizedName];
40
41
  const observedDate = platform.observedAt ? new Date(platform.observedAt).toLocaleDateString(undefined, {
41
42
  year: 'numeric',
42
43
  month: 'short',
@@ -84,7 +85,7 @@ export default function ProviderInsights({ platforms, insights }: ProviderInsigh
84
85
  </div>
85
86
 
86
87
  {/* Insights */}
87
- {providerInsight && (
88
+ {providerInsight && providerInsight.data_points && (
88
89
  <div className="p-4">
89
90
  {/* Summary */}
90
91
  <p className={'text-sm mb-4'} style={{ color: 'var(--text-secondary)' }}>
@@ -26,15 +26,15 @@ interface ReportHeaderProps {
26
26
  badgeId: string | undefined;
27
27
  developerName: string | undefined;
28
28
  updatedAt: string | undefined;
29
- score: number | undefined;
29
+ score?: number | undefined;
30
30
  isPublic: boolean;
31
31
  badgeImageUrl: string;
32
32
  }
33
33
 
34
34
  const ReportHeader = ({ badgeId, developerName, updatedAt, score = 0, isPublic, badgeImageUrl }: ReportHeaderProps) => {
35
35
  // Use the dynamic image if available, otherwise fall back to the score-based one.
36
- const finalBadgeImageUrl = badgeImageUrl || getBadgeImageUrl(score);
37
- const tint = hexToRgba(pickTint(score), 0.06);
36
+ const finalBadgeImageUrl = badgeImageUrl || getBadgeImageUrl(score || 0);
37
+ const tint = hexToRgba(pickTint(score || 0), 0.06);
38
38
 
39
39
  const formattedDate = updatedAt ? new Date(updatedAt).toLocaleString(undefined, {
40
40
  year: 'numeric',
package/src/types.ts CHANGED
@@ -26,7 +26,7 @@ export interface PublicBadgeData {
26
26
  assessmentResult: AssessmentResult;
27
27
  updatedAt: string;
28
28
  badgeImageUrl?: string;
29
- connectedPlatforms?: {
29
+ connectedAccounts?: {
30
30
  name: string;
31
31
  url?: string;
32
32
  handle?: string;
@@ -59,12 +59,12 @@ export type User = {
59
59
  latestBadgeImageUrl?: string;
60
60
  isPublic?: boolean;
61
61
  assessments: AssessmentSummary[];
62
- connectedPlatforms?: { name: string; url: string | null; handle: string | null; observedAt?: string }[];
62
+ connectedAccounts?: { name: string; url: string | null; handle: string | null; observedAt?: string }[];
63
63
  };
64
64
 
65
65
  export interface SummaryScore {
66
66
  score?: number;
67
- description: string;
67
+ description?: string;
68
68
  descriptor?: string;
69
69
  letter_grade?: string;
70
70
  }
@@ -80,6 +80,12 @@ export interface ProviderInsight {
80
80
  summary: string;
81
81
  }
82
82
 
83
+ export interface TopContributingRule {
84
+ provider: string;
85
+ rule_label: string;
86
+ why: string;
87
+ }
88
+
83
89
  export interface DomainCSVRow {
84
90
  Country?: string;
85
91
  'Entity Type'?: string;
@@ -128,25 +134,25 @@ export interface IpRiskAnalysis {
128
134
  }
129
135
 
130
136
  export interface AssessmentResult {
131
- report_summary: string;
137
+ report_summary?: string;
132
138
  recommendations: {
133
- summary: string;
134
- bullet_points: string[];
139
+ summary?: string;
140
+ bullet_points?: string[];
135
141
  };
136
- developer_trust_explanation: string;
137
- industry_considerations: string;
142
+ developer_trust_explanation?: string;
143
+ industry_considerations?: string;
138
144
  ai_usage_summary: {
139
- explanation: string;
140
- key_findings: string[];
141
- files_with_ai_findings: number;
142
- files_analyzed: number;
143
- };
144
- key_skills: string[];
145
- summary_scores: {
146
- developer_trust: SummaryScore;
147
- risk_score: SummaryScore;
148
- kyd_self_check: SummaryScore;
149
- ai_usage: SummaryScore;
145
+ explanation?: string;
146
+ key_findings?: string[];
147
+ files_with_ai_findings?: number;
148
+ files_analyzed?: number;
149
+ } | null;
150
+ key_skills?: string[];
151
+ summary_scores?: {
152
+ developer_trust?: SummaryScore;
153
+ risk_score?: SummaryScore;
154
+ kyd_self_check?: SummaryScore;
155
+ ai_usage?: SummaryScore;
150
156
  };
151
157
  provider_insights: {
152
158
  [provider: string]: ProviderInsight;
@@ -168,6 +174,9 @@ export interface AssessmentResult {
168
174
  csl_details?: any;
169
175
  fbi_matches?: any[];
170
176
  };
177
+ top_contributing_rules?: TopContributingRule[];
178
+ rules_graph_s3_key?: string;
179
+ rules_digest?: any;
171
180
  optOutScreening?: boolean;
172
181
  clientMetadata?: ClientMetadata;
173
182
  }