kyd-shared-badge 0.3.108 → 0.3.110

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.3.108",
3
+ "version": "0.3.110",
4
4
  "private": false,
5
5
  "main": "./src/index.ts",
6
6
  "module": "./src/index.ts",
@@ -139,6 +139,7 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless }: { badgeData: Pub
139
139
  countries={(assessmentResult?.screening_sources?.ip_risk_analysis?.raw_data?.countries) || []}
140
140
  accountAuthenticity={assessmentResult?.account_authenticity}
141
141
  companyName={badgeData.companyName}
142
+ rightBadgeLayout={!assessmentResult?.enterprise_match}
142
143
  />
143
144
  </Reveal>
144
145
  {/* Coaching / Evidence under header when present */}
@@ -52,7 +52,7 @@ type ChatWidgetProps = Partial<{
52
52
  // return `rgba(${r}, ${g}, ${b}, ${alpha})`;
53
53
  // };
54
54
 
55
- const SharedBadgeDisplay = ({ badgeData, chatProps, headless }: { badgeData: PublicBadgeData, chatProps?: ChatWidgetProps, headless?: boolean }) => {
55
+ const SharedBadgeDisplay = ({ badgeData, chatProps, headless, selfCheck = false }: { badgeData: PublicBadgeData, chatProps?: ChatWidgetProps, headless?: boolean, selfCheck?: boolean }) => {
56
56
  const {
57
57
  badgeId,
58
58
  developerName,
@@ -91,18 +91,18 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless }: { badgeData: Pub
91
91
  }
92
92
  })();
93
93
 
94
- const topBusinessForGenre = (genre: string) => {
95
- const cats: string[] = (genreMapping)?.[genre] || [];
96
- const items = [];
97
- for (const c of cats) {
98
- const arr = (categoryTopByGraph)?.[c] || [];
99
- for (const it of arr) items.push(it);
100
- }
101
- items.sort((a, b) => Number(b?.weight || 0) - Number(a?.weight || 0));
102
- return items.slice(0, 3) as TopBusinessRule[];
103
- };
104
-
105
- const skillsMatrix = assessmentResult?.skills_matrix || { skills: [] };
94
+ // const topBusinessForGenre = (genre: string) => {
95
+ // const cats: string[] = (genreMapping)?.[genre] || [];
96
+ // const items = [];
97
+ // for (const c of cats) {
98
+ // const arr = (categoryTopByGraph)?.[c] || [];
99
+ // for (const it of arr) items.push(it);
100
+ // }
101
+ // items.sort((a, b) => Number(b?.weight || 0) - Number(a?.weight || 0));
102
+ // return items.slice(0, 3) as TopBusinessRule[];
103
+ // };
104
+
105
+ // const skillsMatrix = assessmentResult?.skills_matrix || { skills: [] };
106
106
  const skillsAll = assessmentResult?.skills_all || { skills: [] };
107
107
  const connected = badgeData?.connectedAccounts || [];
108
108
 
@@ -225,6 +225,8 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless }: { badgeData: Pub
225
225
  accountAuthenticity={assessmentResult?.account_authenticity}
226
226
  companyName={badgeData.companyName}
227
227
  sourcesProviders={(badgeData?.connectedAccounts || []).map(a => (a?.name || '').toLowerCase())}
228
+ selfCheck={selfCheck}
229
+ rightBadgeLayout={!hasEnterpriseMatch}
228
230
  />
229
231
  </div>
230
232
  {/* Top-right: Role match section */}
@@ -258,6 +260,7 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless }: { badgeData: Pub
258
260
  percent={techPct}
259
261
  label={techLabel}
260
262
  topMovers={[]}
263
+ selfCheck={selfCheck}
261
264
  />
262
265
  );
263
266
  })()}
@@ -4,7 +4,7 @@ import React, { useMemo } from 'react';
4
4
  import GaugeComponent from '@knowyourdeveloper/react-gauge-component';
5
5
  import BusinessRuleLink from './BusinessRuleLink';
6
6
  import { FiInfo } from 'react-icons/fi';
7
- import { hexToRgba, scoreToColorHex, clampPercent, red, yellow, green } from '../colors';
7
+ import { hexToRgba, scoreToColorHex, clampPercent, red, yellow, green, green1, green2, green3, green4, green5 } from '../colors';
8
8
 
9
9
  type TopMover = { label?: string; uid?: string };
10
10
 
@@ -18,6 +18,7 @@ export default function GaugeCard({
18
18
  topMovers,
19
19
  topMoversTitle,
20
20
  tooltipText,
21
+ selfCheck,
21
22
  }: {
22
23
  title: string;
23
24
  description?: string;
@@ -26,10 +27,18 @@ export default function GaugeCard({
26
27
  topMovers?: TopMover[];
27
28
  topMoversTitle?: string;
28
29
  tooltipText?: string;
30
+ selfCheck?: boolean;
29
31
  }) {
30
32
  const pct = clampPercent(percent);
31
33
  const displayLabel = label || '';
32
- const headerTint = hexToRgba(scoreToColorHex(pct), 0.06);
34
+ const shadeFor = (p: number) => {
35
+ if (p < 20) return green5;
36
+ if (p < 40) return green4;
37
+ if (p < 60) return green3;
38
+ if (p < 80) return green2;
39
+ return green1;
40
+ };
41
+ const headerTint = hexToRgba(selfCheck ? shadeFor(pct) : scoreToColorHex(pct), 0.06);
33
42
 
34
43
  // Technical evidence tiers from backend thresholds
35
44
  const tickLabels = useMemo(() => ({
@@ -65,10 +74,8 @@ export default function GaugeCard({
65
74
  >
66
75
  <div className="mb-3 flex items-start justify-between gap-2 pt-5 px-5">
67
76
  <div>
68
- <div className={'font-semibold'} style={{ color: 'var(--text-main)' }}>{title}</div>
69
- {description ? (
70
- <div className={'text-xs mt-1'} style={{ color: 'var(--text-secondary)' }}>{description}</div>
71
- ) : null}
77
+ <div className={'font-semibold text-xl'} style={{ color: 'var(--text-main)' }}>{title}</div>
78
+ <div className={'text-sm mt-1'} style={{ color: 'var(--text-secondary)' }}>{description}</div>
72
79
  </div>
73
80
  {(tooltipText || description) && (
74
81
  <span className={'relative inline-flex items-center group cursor-help'} style={{ color: 'var(--text-secondary)' }}>
@@ -103,15 +110,22 @@ export default function GaugeCard({
103
110
  },
104
111
  tickLabels: tickLabels,
105
112
  }}
106
- arc={{
107
- padding: 0.02,
108
- // Explicit subArcs ensure left->right map: red -> yellow -> green
109
- subArcs: [
110
- { limit: 33, color: red },
111
- { limit: 66, color: yellow },
112
- { limit: 100, color: green },
113
- ],
114
- }}
113
+ arc={{
114
+ padding: 0.02,
115
+ subArcs: selfCheck
116
+ ? [
117
+ { limit: 20, color: green5 },
118
+ { limit: 40, color: green4 },
119
+ { limit: 60, color: green3 },
120
+ { limit: 80, color: green2 },
121
+ { limit: 100, color: green1 },
122
+ ]
123
+ : [
124
+ { limit: 33, color: red },
125
+ { limit: 66, color: yellow },
126
+ { limit: 100, color: green },
127
+ ],
128
+ }}
115
129
  pointer={{ type: 'blob', elastic: true, animationDelay: 0 }}
116
130
  />
117
131
  {(tooltipText || description) && (
@@ -6,6 +6,7 @@ import countriesLib from 'i18n-iso-countries';
6
6
  import enLocale from 'i18n-iso-countries/langs/en.json';
7
7
  import { FiInfo, FiAlertTriangle } from 'react-icons/fi';
8
8
  import { ProviderIcon } from '../utils/provider';
9
+ import { green1, green2, green3, green4, green5 } from '../colors';
9
10
 
10
11
  // Register English locale once at module import time
11
12
  countriesLib.registerLocale(enLocale);
@@ -24,7 +25,14 @@ const hexToRgba = (hex: string, alpha: number) => {
24
25
  return `rgba(${r}, ${g}, ${b}, ${alpha})`;
25
26
  };
26
27
 
27
- const pickTint = (score: number) => {
28
+ const pickTint = (score: number, selfCheck?: boolean) => {
29
+ if (selfCheck) {
30
+ if (score < 20) return green5;
31
+ if (score < 40) return green4;
32
+ if (score < 60) return green3;
33
+ if (score < 80) return green2;
34
+ return green1;
35
+ }
28
36
  if (score >= 75) return '#02a389';
29
37
  if (score >= 50) return '#ffbb54';
30
38
  return '#EC6662';
@@ -43,12 +51,14 @@ interface ReportHeaderProps {
43
51
  accountAuthenticity?: { label?: string; description?: string };
44
52
  companyName?: string;
45
53
  sourcesProviders?: string[];
54
+ selfCheck?: boolean;
55
+ rightBadgeLayout?: boolean;
46
56
  }
47
57
 
48
- const ReportHeader = ({ badgeId, developerName, updatedAt, score = 0, badgeImageUrl, summary, enterpriseMatch, countries = [], accountAuthenticity, companyName, sourcesProviders = [] }: ReportHeaderProps) => {
58
+ const ReportHeader = ({ badgeId, developerName, updatedAt, score = 0, badgeImageUrl, summary, enterpriseMatch, countries = [], accountAuthenticity, companyName, sourcesProviders = [], selfCheck, rightBadgeLayout }: ReportHeaderProps) => {
49
59
  // Use the dynamic image if available, otherwise fall back to the score-based one.
50
60
  const finalBadgeImageUrl = badgeImageUrl || getBadgeImageUrl(score || 0);
51
- const tint = hexToRgba(pickTint(score || 0), 0.06);
61
+ const tint = hexToRgba(pickTint(score || 0, selfCheck), 0.06);
52
62
  const matchLabel = enterpriseMatch?.label;
53
63
  const sources = (() => {
54
64
  const primary = Array.isArray(sourcesProviders) ? sourcesProviders : [];
@@ -67,7 +77,7 @@ const ReportHeader = ({ badgeId, developerName, updatedAt, score = 0, badgeImage
67
77
 
68
78
  return (
69
79
  <div
70
- className={'p-6 rounded-md shadow-md border'}
80
+ className={'p-6 rounded-md border'}
71
81
  style={{ backgroundColor: 'var(--content-card-background)', borderColor: 'var(--icon-button-secondary)', backgroundImage: `linear-gradient(${tint}, ${tint})` }}
72
82
  >
73
83
  {(() => {
@@ -89,9 +99,9 @@ const ReportHeader = ({ badgeId, developerName, updatedAt, score = 0, badgeImage
89
99
  </div>
90
100
  );
91
101
  })()}
92
- <div className="flex flex-col gap-3">
102
+ <div className={`flex gap-3 ${rightBadgeLayout ? 'flex-col md:flex-row items-center justify-between' : 'flex-col'}`}>
93
103
  {/* Info section: Title, Candidate, Details and Summary */}
94
- <div className="w-full">
104
+ <div className={`w-full ${rightBadgeLayout ? 'md:flex-1' : ''}`}>
95
105
  <div className='space-y-2'>
96
106
  <span className='flex gap-2 w-full items-end text-start justify-start'>
97
107
  <h2 className={'text-xl font-light'} style={{ color: 'var(--text-main)' }}>KYD Candidate:</h2>
@@ -179,11 +189,18 @@ const ReportHeader = ({ badgeId, developerName, updatedAt, score = 0, badgeImage
179
189
  </div>
180
190
 
181
191
  {/* Badge Image with robust centered overlay */}
182
- <div className="w-full flex items-center justify-center self-stretch">
183
- <div className="relative w-full max-w-xs select-none">
184
- <Image src={finalBadgeImageUrl} alt="KYD Badge" width={100} height={100} priority className='w-full h-auto pointer-events-none p-10'/>
185
- <div className="pointer-events-none absolute left-1/2 top-[66%] -translate-x-1/2 -translate-y-1/2">
186
- <div className="font-extrabold text-black text-3xl ">
192
+ <div className={`flex items-center ${rightBadgeLayout ? 'md:justify-end h-20' : 'justify-center w-full mt-4'} `}>
193
+ <div className={`relative w-full ${rightBadgeLayout ? 'max-w-[85px]' : 'px-20'} select-none`}>
194
+ <Image
195
+ src={finalBadgeImageUrl}
196
+ alt="KYD Badge"
197
+ width={rightBadgeLayout ? 25 : 100}
198
+ height={rightBadgeLayout ? 25 : 100}
199
+ priority
200
+ className='w-full h-auto pointer-events-none'
201
+ />
202
+ <div className={`pointer-events-none absolute left-1/2 -translate-x-1/2 -translate-y-1/2 ${rightBadgeLayout ? 'top-[72%]' : 'top-[69%]'}`}>
203
+ <div className={`font-extrabold text-black ${rightBadgeLayout ? 'text-xs' : 'text-3xl'}`}>
187
204
  {Math.round(score || 0)}%
188
205
  </div>
189
206
  </div>
@@ -58,9 +58,9 @@ export default function RiskCard({
58
58
  >
59
59
  <div className="mb-3 flex items-start justify-between gap-2">
60
60
  <div>
61
- <div className={'font-semibold'} style={{ color: 'var(--text-main)' }}>{title}</div>
61
+ <div className={'font-semibold text-xl'} style={{ color: 'var(--text-main)' }}>{title}</div>
62
62
  {description ? (
63
- <div className={'text-xs mt-1'} style={{ color: 'var(--text-secondary)' }}>{description}</div>
63
+ <div className={'text-sm mt-1'} style={{ color: 'var(--text-secondary)' }}>{description}</div>
64
64
  ) : null}
65
65
  </div>
66
66
  {(tooltipText || description) && (
@@ -31,7 +31,7 @@ export default function RoleOverviewCard({
31
31
 
32
32
  const tickLabels = useMemo(() => ({
33
33
  type: 'outer' as const,
34
- hideMinMax: false,
34
+ hideMinMax: true,
35
35
  defaultTickLineConfig: {
36
36
  length: 7,
37
37
  width: 1,
@@ -45,11 +45,9 @@ export default function RoleOverviewCard({
45
45
  }
46
46
  },
47
47
  ticks: [
48
- { value: 0, valueConfig: { formatTextValue: () => 'Very Weak' } }, // Incompatible is too long, gets cut off
49
- { value: 25, valueConfig: { formatTextValue: () => 'Weak' } },
48
+ { value: 16, valueConfig: { formatTextValue: () => 'Weak' } },
50
49
  { value: 50, valueConfig: { formatTextValue: () => 'Partial' } },
51
- { value: 75, valueConfig: { formatTextValue: () => 'Strong' } },
52
- { value: 100, valueConfig: { formatTextValue: () => 'Optimal' } },
50
+ { value: 83, valueConfig: { formatTextValue: () => 'Strong' } },
53
51
  ]
54
52
  }), []);
55
53
 
@@ -262,7 +262,6 @@ export default function SkillsBubble({ skillsCategoryRadar, skillsByCategory, sk
262
262
  {entry.label}
263
263
  {/* Evidence count bullet */}
264
264
  {(() => {
265
- console.log('entry', entry);
266
265
  const meta = skillsMeta?.[entry.label];
267
266
  const count = Number((meta as any)?.evidenceCount || 0);
268
267
  return count > 0 ? (
@@ -410,11 +409,11 @@ export default function SkillsBubble({ skillsCategoryRadar, skillsByCategory, sk
410
409
  }}
411
410
  >
412
411
  <div className="flex items-center gap-2">
413
- <span className="inline-block h-3 w-3 rounded-full" style={{ background: green1 }} />
412
+ <span className="inline-block h-3 w-3 rounded-full" style={{ background: green5 }} />
414
413
  <span>Size = evidence count per category</span>
415
414
  </div>
416
415
  <div className="flex items-center gap-2 mt-1">
417
- <span className="inline-block h-3 w-3 rounded-full" style={{ background: green5 }} />
416
+ <span className="inline-block h-3 w-3 rounded-full border border-[var(--text-secondary)]" style={{ background: green1 }} />
418
417
  <span>Color = experience (darker = more)</span>
419
418
  </div>
420
419
  </div>