kyd-shared-badge 0.1.2 → 0.1.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kyd-shared-badge",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "private": false,
5
5
  "main": "./src/index.ts",
6
6
  "module": "./src/index.ts",
@@ -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 className="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">
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 text-neutral-800 dark:text-white mb-2 ${scoreType === 'descriptor' ? 'text-neutral-400' : ''}`}>{title}</h3>
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="text-sm text-neutral-600 dark:text-neutral-400 mt-4">{description}</p>
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
  };
@@ -106,6 +114,7 @@ const SharedBadgeDisplay = ({ badgeData, type = 'individual' }: { badgeData: Pub
106
114
  description={devTrustScore?.description || ''}
107
115
  icon={FiThumbsUp}
108
116
  scoreType='number'
117
+ isRecruiter={isRecruiter}
109
118
  />
110
119
  <ScoreCard
111
120
  title="KYD Risk™"
@@ -113,6 +122,7 @@ const SharedBadgeDisplay = ({ badgeData, type = 'individual' }: { badgeData: Pub
113
122
  description={riskScore?.description || ''}
114
123
  icon={FiShield}
115
124
  scoreType='risk'
125
+ isRecruiter={isRecruiter}
116
126
  />
117
127
  <ScoreCard
118
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>}
@@ -121,6 +131,7 @@ const SharedBadgeDisplay = ({ badgeData, type = 'individual' }: { badgeData: Pub
121
131
  descriptor={aiUsageScore?.descriptor}
122
132
  icon={FiCpu}
123
133
  scoreType='descriptor'
134
+ isRecruiter={isRecruiter}
124
135
  />
125
136
  </div>
126
137
 
@@ -214,6 +225,7 @@ const SharedBadgeDisplay = ({ badgeData, type = 'individual' }: { badgeData: Pub
214
225
  sources={[...(screening_sources.ofac_lists || []), ...(screening_sources.additional_watchlists || [])]}
215
226
  searchedAt={updatedAt}
216
227
  developerName={developerName || 'this developer'}
228
+ view={type}
217
229
  />
218
230
  </div>
219
231
  <div>
@@ -223,6 +235,7 @@ const SharedBadgeDisplay = ({ badgeData, type = 'individual' }: { badgeData: Pub
223
235
  sources={screening_sources.risk_profile_domains || []}
224
236
  searchedAt={updatedAt}
225
237
  developerName={developerName || 'this developer'}
238
+ view={type}
226
239
  />
227
240
  </div>
228
241
  </div>
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import React, { useState } from 'react';
4
- import { DomainCSVRow } from '../lib/types';
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="hover:bg-neutral-50 dark:hover:bg-neutral-800/50 transition-colors">
29
- <td className="px-4 py-4 whitespace-nowrap text-sm font-medium text-neutral-800 dark:text-neutral-200">
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="px-4 py-4 whitespace-normal text-sm text-neutral-600 dark:text-neutral-300">
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="px-4 py-4 whitespace-nowrap text-sm text-neutral-600 dark:text-neutral-300">
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="px-4 py-4 whitespace-nowrap text-sm text-neutral-600 dark:text-neutral-300">
39
- <span className="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">
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="px-4 py-4 text-sm text-neutral-600 dark:text-neutral-300 whitespace-normal">
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="hover:bg-neutral-50 dark:hover:bg-neutral-800/50 transition-colors">
51
- <td className="px-4 py-4 whitespace-normal text-sm text-neutral-600 dark:text-neutral-300">{source.country || 'N/A'}</td>
52
- <td className="px-4 py-4 whitespace-normal text-sm text-neutral-600 dark:text-neutral-300">{source.entityType || 'N/A'}</td>
53
- <td className="px-4 py-4 whitespace-normal text-sm font-medium text-neutral-800 dark:text-neutral-200">{source.entityName || source.url}</td>
54
- <td className="px-4 py-4 whitespace-nowrap text-sm text-neutral-600 dark:text-neutral-300">{searchedAt}</td>
55
- <td className="px-4 py-4 whitespace-nowrap text-sm text-neutral-600 dark:text-neutral-300">
56
- <span className="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">
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="px-4 py-4 text-sm text-neutral-600 dark:text-neutral-300 whitespace-normal">
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="overflow-x-auto rounded-lg border border-neutral-200 dark:border-neutral-700">
104
- <table className="min-w-full divide-y divide-neutral-200 dark:divide-neutral-700">
105
- <thead className="bg-neutral-50 dark:bg-neutral-800">
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="px-4 py-3 text-left text-xs font-semibold text-neutral-500 dark:text-neutral-300 uppercase tracking-wider">
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="bg-white dark:bg-neutral-900 divide-y divide-neutral-200 dark:divide-neutral-700">
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="mt-4 flex items-center justify-between text-sm text-neutral-600 dark:text-neutral-400">
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="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"
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>
@@ -48,7 +48,7 @@ const ReportHeader = ({ badgeId, developerName, updatedAt, score = 0, isPublic,
48
48
  KYD Self-Check™
49
49
  </h1>
50
50
  <p className={isRecruiter ? 'text-sm' : 'text-sm text-neutral-600 dark:text-neutral-400'} style={isRecruiter ? { color: 'var(--text-secondary)' } : undefined}>
51
- {isPublic ? 'Public Report' : 'Private Report'}
51
+ {(isPublic && type === 'individual') ? 'Public Report' : 'Private Report'}
52
52
  </p>
53
53
  </div>
54
54
  </div>