@learncard/react 2.10.7 → 2.11.0

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.
Files changed (130) hide show
  1. package/dist/cjs/{BoostGenericCard-99a3ab28.js → BoostGenericCard-feaf99bd.js} +47 -7
  2. package/dist/cjs/BoostGenericCard-feaf99bd.js.map +1 -0
  3. package/dist/cjs/{CertificateDisplayCard-b673e66d.js → CertificateDisplayCard-de7d7318.js} +4 -4
  4. package/dist/cjs/{CertificateDisplayCard-b673e66d.js.map → CertificateDisplayCard-de7d7318.js.map} +1 -1
  5. package/dist/cjs/{MeritBadgeDisplayCard-1de23221.js → MeritBadgeDisplayCard-6edd0a69.js} +5 -5
  6. package/dist/cjs/{MeritBadgeDisplayCard-1de23221.js.map → MeritBadgeDisplayCard-6edd0a69.js.map} +1 -1
  7. package/dist/cjs/{MeritBadgeSkillsCount-f62ad753.js → MeritBadgeSkillsCount-7ac48ce3.js} +2 -2
  8. package/dist/cjs/{MeritBadgeSkillsCount-f62ad753.js.map → MeritBadgeSkillsCount-7ac48ce3.js.map} +1 -1
  9. package/dist/cjs/{VCCard-b5d1ecb3.js → VCCard-0b85bc7d.js} +3 -3
  10. package/dist/cjs/{VCCard-b5d1ecb3.js.map → VCCard-0b85bc7d.js.map} +1 -1
  11. package/dist/cjs/{VCDisplayBackFace-aab4d52c.js → VCDisplayBackFace-45a8d94f.js} +3 -3
  12. package/dist/cjs/{VCDisplayBackFace-aab4d52c.js.map → VCDisplayBackFace-45a8d94f.js.map} +1 -1
  13. package/dist/cjs/{VCDisplayCard-95b7f7dc.js → VCDisplayCard-9e43c7db.js} +3 -3
  14. package/dist/cjs/{VCDisplayCard-95b7f7dc.js.map → VCDisplayCard-9e43c7db.js.map} +1 -1
  15. package/dist/cjs/{VCDisplayCard2-ede5a360.js → VCDisplayCard2-81d04ca4.js} +33 -17
  16. package/dist/cjs/VCDisplayCard2-81d04ca4.js.map +1 -0
  17. package/dist/cjs/{VCVerificationPill-b5824def.js → VCVerificationPill-9416f420.js} +2 -2
  18. package/dist/cjs/{VCVerificationPill-b5824def.js.map → VCVerificationPill-9416f420.js.map} +1 -1
  19. package/dist/cjs/{VerifierStateBadgeAndText-c15064b2.js → VerifierStateBadgeAndText-54065ad8.js} +123 -4
  20. package/dist/cjs/VerifierStateBadgeAndText-54065ad8.js.map +1 -0
  21. package/dist/cjs/{credential.helpers-98fc74d8.js → credential.helpers-cf5b7e3c.js} +2 -2
  22. package/dist/cjs/{credential.helpers-98fc74d8.js.map → credential.helpers-cf5b7e3c.js.map} +1 -1
  23. package/dist/cjs/i18n-8fd90b2f.js +58 -0
  24. package/dist/cjs/i18n-8fd90b2f.js.map +1 -0
  25. package/dist/cjs/index-5488badc.js.map +1 -1
  26. package/dist/cjs/index.js +19 -14
  27. package/dist/cjs/index.js.map +1 -1
  28. package/dist/cjs/index14.js +13 -12
  29. package/dist/cjs/index14.js.map +1 -1
  30. package/dist/cjs/index22.js +6 -5
  31. package/dist/cjs/index22.js.map +1 -1
  32. package/dist/cjs/index39.js +11 -10
  33. package/dist/cjs/index39.js.map +1 -1
  34. package/dist/cjs/index4.js +4 -3
  35. package/dist/cjs/index4.js.map +1 -1
  36. package/dist/cjs/index40.js +4 -4
  37. package/dist/cjs/index41.js +5 -5
  38. package/dist/cjs/index42.js +7 -6
  39. package/dist/cjs/index42.js.map +1 -1
  40. package/dist/cjs/index43.js +1 -1
  41. package/dist/cjs/index46.js +2 -2
  42. package/dist/cjs/index7.js +4 -4
  43. package/dist/cjs/{types.esm-f1680b31.js → types.esm-e775534b.js} +94 -2
  44. package/dist/cjs/types.esm-e775534b.js.map +1 -0
  45. package/dist/esm/{AchievementCard-c600788a.js → AchievementCard-0f183e9d.js} +2 -2
  46. package/dist/esm/{AchievementCard-c600788a.js.map → AchievementCard-0f183e9d.js.map} +1 -1
  47. package/dist/esm/{BoostGenericCard-7dfcc887.js → BoostGenericCard-f0c9952c.js} +48 -9
  48. package/dist/esm/BoostGenericCard-f0c9952c.js.map +1 -0
  49. package/dist/esm/{BoostSmallCard-a73b6800.js → BoostSmallCard-3dedb5ae.js} +2 -2
  50. package/dist/esm/{BoostSmallCard-a73b6800.js.map → BoostSmallCard-3dedb5ae.js.map} +1 -1
  51. package/dist/esm/{CertificateDisplayCard-4e9254e0.js → CertificateDisplayCard-34e0b6a8.js} +4 -4
  52. package/dist/esm/{CertificateDisplayCard-4e9254e0.js.map → CertificateDisplayCard-34e0b6a8.js.map} +1 -1
  53. package/dist/esm/{GenericCard-a469f2f0.js → GenericCard-b40c0617.js} +2 -2
  54. package/dist/esm/{GenericCard-a469f2f0.js.map → GenericCard-b40c0617.js.map} +1 -1
  55. package/dist/esm/{JobListCard-e8b4ab33.js → JobListCard-dfb94830.js} +2 -2
  56. package/dist/esm/{JobListCard-e8b4ab33.js.map → JobListCard-dfb94830.js.map} +1 -1
  57. package/dist/esm/{LearnPill-e0720485.js → LearnPill-ce240a4a.js} +2 -2
  58. package/dist/esm/{LearnPill-e0720485.js.map → LearnPill-ce240a4a.js.map} +1 -1
  59. package/dist/esm/{MeritBadgeDisplayCard-a7a504e0.js → MeritBadgeDisplayCard-c6f65ac6.js} +5 -5
  60. package/dist/esm/{MeritBadgeDisplayCard-a7a504e0.js.map → MeritBadgeDisplayCard-c6f65ac6.js.map} +1 -1
  61. package/dist/esm/{MeritBadgeSkillsCount-c5271d7f.js → MeritBadgeSkillsCount-c51c5111.js} +2 -2
  62. package/dist/esm/{MeritBadgeSkillsCount-c5271d7f.js.map → MeritBadgeSkillsCount-c51c5111.js.map} +1 -1
  63. package/dist/esm/{VCCard-f017f1ee.js → VCCard-6c85ef2e.js} +3 -3
  64. package/dist/esm/{VCCard-f017f1ee.js.map → VCCard-6c85ef2e.js.map} +1 -1
  65. package/dist/esm/{VCDisplayBackFace-bb60a881.js → VCDisplayBackFace-211f4bfb.js} +3 -3
  66. package/dist/esm/{VCDisplayBackFace-bb60a881.js.map → VCDisplayBackFace-211f4bfb.js.map} +1 -1
  67. package/dist/esm/{VCDisplayCard-9905d9d3.js → VCDisplayCard-04660178.js} +3 -3
  68. package/dist/esm/{VCDisplayCard-9905d9d3.js.map → VCDisplayCard-04660178.js.map} +1 -1
  69. package/dist/esm/{VCDisplayCard2-72bed598.js → VCDisplayCard2-d4594742.js} +33 -17
  70. package/dist/esm/VCDisplayCard2-d4594742.js.map +1 -0
  71. package/dist/esm/{VCVerificationPill-83eb61f4.js → VCVerificationPill-eafbb82e.js} +2 -2
  72. package/dist/esm/{VCVerificationPill-83eb61f4.js.map → VCVerificationPill-eafbb82e.js.map} +1 -1
  73. package/dist/esm/{VerifierStateBadgeAndText-60b98d25.js → VerifierStateBadgeAndText-2859597e.js} +123 -4
  74. package/dist/esm/VerifierStateBadgeAndText-2859597e.js.map +1 -0
  75. package/dist/esm/{constants-f1ed610c.js → constants-010faf74.js} +2 -2
  76. package/dist/esm/{constants-f1ed610c.js.map → constants-010faf74.js.map} +1 -1
  77. package/dist/esm/{credential.helpers-00099c70.js → credential.helpers-845e8c09.js} +2 -2
  78. package/dist/esm/{credential.helpers-00099c70.js.map → credential.helpers-845e8c09.js.map} +1 -1
  79. package/dist/esm/i18n-08b606cb.js +50 -0
  80. package/dist/esm/i18n-08b606cb.js.map +1 -0
  81. package/dist/esm/index-28917993.js.map +1 -1
  82. package/dist/esm/{index-cbe3a76a.js → index-7542e9ee.js} +2 -2
  83. package/dist/esm/{index-cbe3a76a.js.map → index-7542e9ee.js.map} +1 -1
  84. package/dist/esm/index.js +21 -20
  85. package/dist/esm/index.js.map +1 -1
  86. package/dist/esm/index13.js +2 -2
  87. package/dist/esm/index14.js +20 -19
  88. package/dist/esm/index14.js.map +1 -1
  89. package/dist/esm/index16.js +2 -2
  90. package/dist/esm/index19.js +2 -2
  91. package/dist/esm/index2.js +2 -2
  92. package/dist/esm/index22.js +6 -5
  93. package/dist/esm/index22.js.map +1 -1
  94. package/dist/esm/index30.js +2 -2
  95. package/dist/esm/index39.js +11 -10
  96. package/dist/esm/index39.js.map +1 -1
  97. package/dist/esm/index4.js +5 -4
  98. package/dist/esm/index4.js.map +1 -1
  99. package/dist/esm/index40.js +4 -4
  100. package/dist/esm/index41.js +5 -5
  101. package/dist/esm/index42.js +7 -6
  102. package/dist/esm/index42.js.map +1 -1
  103. package/dist/esm/index43.js +1 -1
  104. package/dist/esm/index46.js +2 -2
  105. package/dist/esm/index5.js +2 -2
  106. package/dist/esm/index7.js +4 -4
  107. package/dist/esm/{types.esm-d6b48609.js → types.esm-5b5092be.js} +94 -2
  108. package/dist/esm/types.esm-5b5092be.js.map +1 -0
  109. package/dist/index.d.ts +60 -2
  110. package/dist/main.css +1 -1
  111. package/dist/main.js +1 -1
  112. package/package.json +3 -3
  113. package/src/components/BoostGenericCard/BoostGenericCard.tsx +53 -19
  114. package/src/components/MeritBadgeDisplayCard/MeritBadgeFrontFace.tsx +1 -1
  115. package/src/components/MeritBadgeDisplayCard/VerifierStateBadgeAndText.tsx +7 -5
  116. package/src/components/VCDisplayCard2/VerificationRow.tsx +28 -4
  117. package/src/components/VCDisplayCard2/VerificationsBox.tsx +4 -5
  118. package/src/helpers/lifecycle.helpers.ts +38 -0
  119. package/src/i18n/index.test.ts +10 -0
  120. package/src/i18n/index.tsx +90 -0
  121. package/src/index.ts +6 -0
  122. package/src/types/index.ts +1 -0
  123. package/dist/cjs/BoostGenericCard-99a3ab28.js.map +0 -1
  124. package/dist/cjs/VCDisplayCard2-ede5a360.js.map +0 -1
  125. package/dist/cjs/VerifierStateBadgeAndText-c15064b2.js.map +0 -1
  126. package/dist/cjs/types.esm-f1680b31.js.map +0 -1
  127. package/dist/esm/BoostGenericCard-7dfcc887.js.map +0 -1
  128. package/dist/esm/VCDisplayCard2-72bed598.js.map +0 -1
  129. package/dist/esm/VerifierStateBadgeAndText-60b98d25.js.map +0 -1
  130. package/dist/esm/types.esm-d6b48609.js.map +0 -1
@@ -6,6 +6,8 @@ import AlignmentSkillsCount from './AlignmentSkillsCount';
6
6
  import ThreeDotVertical from '../svgs/ThreeDotVertical';
7
7
  import { CircleCheckButton } from '../CircleCheckButton';
8
8
  import BadgeThumbnailImg from '../BadgeThumbnailImg/BadgeThumbnailImg';
9
+ import { getLifecycleTreatment } from '../../helpers/lifecycle.helpers';
10
+ import { useT } from '../../i18n';
9
11
 
10
12
  export const BoostGenericCard: React.FC<BoostGenericCardProps> = ({
11
13
  title,
@@ -33,7 +35,9 @@ export const BoostGenericCard: React.FC<BoostGenericCardProps> = ({
33
35
  linkedCredentialsCount = 0,
34
36
  linkedCredentialsClassName = '',
35
37
  checkBtnClass = '',
38
+ lifecycleStatus = 'active',
36
39
  }) => {
40
+ const t = useT();
37
41
  const thumbClass = TYPE_TO_WALLET_DARK_COLOR[type]
38
42
  ? `bg-${TYPE_TO_WALLET_DARK_COLOR[type]}`
39
43
  : 'bg-grayscale-50';
@@ -45,10 +49,31 @@ export const BoostGenericCard: React.FC<BoostGenericCardProps> = ({
45
49
 
46
50
  const DisplayIcon = getDisplayIcon(displayType as DisplayTypeEnum);
47
51
 
52
+ // Shared revoked/suspended treatment (see getLifecycleTreatment). Inline styles are
53
+ // used (not Tailwind classes) because the consumer app's Tailwind doesn't scan this
54
+ // package and it ships no utility CSS, so utility classes here have no backing rule.
55
+ const {
56
+ isInactive,
57
+ mediaStyle: inactiveMediaStyle,
58
+ textStyle: inactiveTextStyle,
59
+ pillBg,
60
+ } = getLifecycleTreatment(lifecycleStatus);
61
+ const pillLabel = t(`credential.lifecycle.${lifecycleStatus}`);
62
+ const pillLeft = showChecked ? 'left-[44px]' : 'left-[8px]';
63
+
48
64
  return (
49
65
  <div
50
66
  className={`flex bg-white flex-col shadow-bottom relative p-0 w-[160px] h-[285px] rounded-[20px] overflow-hidden ${className}`}
51
67
  >
68
+ {isInactive && (
69
+ <span
70
+ className={`absolute top-[8px] ${pillLeft} z-20 rounded-full px-[9px] py-[3px] text-[10px] font-extrabold uppercase tracking-wide text-white`}
71
+ style={{ backgroundColor: pillBg }}
72
+ >
73
+ {pillLabel}
74
+ </span>
75
+ )}
76
+
52
77
  {optionsTriggerOnClick && (
53
78
  <section
54
79
  className="absolute cursor-pointer h-[30px] w-[30px] top-[5px] right-[5px] rounded-full bg-white/70 flex items-center justify-center z-20"
@@ -59,7 +84,10 @@ export const BoostGenericCard: React.FC<BoostGenericCardProps> = ({
59
84
  )}
60
85
 
61
86
  {bgImgSrc && (
62
- <section className="absolute top-[-50px] left-0 rounded-b-full overflow-hidden z-0">
87
+ <section
88
+ className="absolute top-[-50px] left-0 rounded-b-full overflow-hidden z-0"
89
+ style={inactiveMediaStyle}
90
+ >
63
91
  <img className="h-full w-full object-cover" src={bgImgSrc} />
64
92
  </section>
65
93
  )}
@@ -69,23 +97,26 @@ export const BoostGenericCard: React.FC<BoostGenericCardProps> = ({
69
97
  className="z-10 flex flex-col flex-grow"
70
98
  onClick={handleInnerClick}
71
99
  >
72
- {/* Thumbnail */}
73
- {customThumbComponent || (
74
- <section className={defaultThumbClass}>
75
- {thumbImgSrc?.trim() ? (
76
- <BadgeThumbnailImg
77
- className="w-full h-full rounded-full object-cover"
78
- src={thumbImgSrc}
79
- alt="Credential Achievement"
80
- />
81
- ) : (
82
- <img
83
- className="max-w-full p-0 object-cover rounded-full"
84
- src={imgSrc}
85
- />
86
- )}
87
- </section>
88
- )}
100
+ {/* Thumbnail — filter on the wrapper so it desaturates a
101
+ customThumbComponent too, not just the default thumb section. */}
102
+ <div style={inactiveMediaStyle}>
103
+ {customThumbComponent || (
104
+ <section className={defaultThumbClass}>
105
+ {thumbImgSrc?.trim() ? (
106
+ <BadgeThumbnailImg
107
+ className="w-full h-full rounded-full object-cover"
108
+ src={thumbImgSrc}
109
+ alt="Credential Achievement"
110
+ />
111
+ ) : (
112
+ <img
113
+ className="max-w-full p-0 object-cover rounded-full"
114
+ src={imgSrc}
115
+ />
116
+ )}
117
+ </section>
118
+ )}
119
+ </div>
89
120
 
90
121
  {/* Details Section: grows to fill available space */}
91
122
  <section
@@ -107,6 +138,7 @@ export const BoostGenericCard: React.FC<BoostGenericCardProps> = ({
107
138
  : 'text-[16px]'
108
139
  }
109
140
  `}
141
+ style={inactiveTextStyle}
110
142
  title={title}
111
143
  >
112
144
  {title}
@@ -124,7 +156,9 @@ export const BoostGenericCard: React.FC<BoostGenericCardProps> = ({
124
156
  </span>
125
157
  )}
126
158
  {customIssuerName || (
127
- <span className="font-bold">{issuerName}</span>
159
+ <span className="font-bold" style={inactiveTextStyle}>
160
+ {issuerName}
161
+ </span>
128
162
  )}
129
163
  </span>
130
164
  </div>
@@ -159,7 +159,7 @@ export const MeritBadgeFrontFace: React.FC<MeritBadgeFrontFaceProps> = ({
159
159
  >
160
160
  <div className="flex flex-col gap-[5px] w-full">
161
161
  <div className="flex flex-col items-center text-grayscale-900">
162
- {issueeName && (
162
+ {issueeName && !customBodyCardComponent && (
163
163
  <>
164
164
  <span className="flex flex-wrap items-baseline justify-center gap-1 text-[16px] leading-[150%] font-jacques max-w-full overflow-hidden text-ellipsis whitespace-nowrap">
165
165
  {!issueeDisplay.isMissing && issueeDisplay.isDidValue ? (
@@ -3,6 +3,7 @@ import UnknownVerifierBadge from '../svgs/UnknownVerifierBadge';
3
3
  import VerifiedBadge from '../svgs/VerifiedBadge';
4
4
  import PersonBadge from '../svgs/PersonBadge';
5
5
  import RedFlag from '../svgs/RedFlag';
6
+ import { useT } from '../../i18n';
6
7
 
7
8
  const VERIFIER_STATES = {
8
9
  selfVerified: 'Self Issued',
@@ -18,36 +19,37 @@ type VerifierStateBadgeAndTextProps = {
18
19
  };
19
20
 
20
21
  const VerifierStateBadgeAndText: React.FC<VerifierStateBadgeAndTextProps> = ({ verifierState }) => {
22
+ const t = useT();
21
23
  return (
22
24
  <div className="flex justify-center">
23
25
  {verifierState === VERIFIER_STATES.selfVerified && (
24
26
  <span className="uppercase font-poppins text-[12px] font-[500] text-green-dark flex gap-[3px] items-center">
25
27
  <PersonBadge />
26
- Self Issued
28
+ {t('verification.selfIssued')}
27
29
  </span>
28
30
  )}
29
31
  {verifierState === VERIFIER_STATES.trustedVerifier && (
30
32
  <span className="uppercase font-poppins text-[12px] font-[500] text-blue-light flex gap-[3px] items-center">
31
33
  <VerifiedBadge />
32
- Trusted Issuer
34
+ {t('verification.trustedIssuer')}
33
35
  </span>
34
36
  )}
35
37
  {verifierState === VERIFIER_STATES.unknownVerifier && (
36
38
  <span className="uppercase font-poppins text-[12px] font-[500] text-orange-500 flex gap-[3px] items-center">
37
39
  <UnknownVerifierBadge />
38
- Unknown Issuer
40
+ {t('verification.unknownIssuer')}
39
41
  </span>
40
42
  )}
41
43
  {verifierState === VERIFIER_STATES.appIssuer && (
42
44
  <span className="uppercase font-poppins text-[12px] font-[500] text-cyan-600 flex gap-[3px] items-center">
43
45
  <UnknownVerifierBadge />
44
- Trusted App
46
+ {t('verification.appIssuer')}
45
47
  </span>
46
48
  )}
47
49
  {verifierState === VERIFIER_STATES.untrustedVerifier && (
48
50
  <span className="uppercase font-poppins text-[12px] font-[500] text-red-mastercard flex gap-[3px] items-center">
49
51
  <RedFlag />
50
- Untrusted Issuer
52
+ {t('verification.untrustedIssuer')}
51
53
  </span>
52
54
  )}
53
55
  </div>
@@ -8,13 +8,29 @@ import AcuteCheckmark from '../svgs/AcuteCheckmark';
8
8
  import ExclamationPoint from '../svgs/ExclamationPoint';
9
9
  import X from '../svgs/X';
10
10
  import { capitalize } from '../../helpers/string.helpers';
11
+ import { useT } from '../../i18n';
11
12
 
12
13
  type VerificationRowProps = {
13
14
  verification: VerificationItem;
14
15
  };
15
16
 
17
+ /** Map the SDK's English verification messages to i18n keys; unknown messages pass through. */
18
+ const MESSAGE_KEYS: Record<string, string> = {
19
+ Valid: 'valid',
20
+ Invalid: 'invalid',
21
+ 'Not Revoked': 'notRevoked',
22
+ Revoked: 'revoked',
23
+ Suspended: 'suspended',
24
+ 'Does Not Expire': 'doesNotExpire',
25
+ Expired: 'expired',
26
+ Active: 'active',
27
+ 'Boost Credential could not be verified.': 'couldNotVerify',
28
+ 'Boost Credential could not be verified': 'couldNotVerify',
29
+ };
30
+
16
31
  const VerificationRow: React.FC<VerificationRowProps> = ({ verification }) => {
17
32
  const [showInfo, setShowInfo] = useState(false);
33
+ const t = useT();
18
34
  const statusColor = getColorForVerificationStatus(verification.status);
19
35
 
20
36
  const getIcon = () => {
@@ -28,11 +44,19 @@ const VerificationRow: React.FC<VerificationRowProps> = ({ verification }) => {
28
44
  }
29
45
  };
30
46
 
47
+ const statusText = t(`verification.status.${String(verification.status).toLowerCase()}`);
48
+ const translateMessage = (msg?: string): string => {
49
+ if (!msg) return '';
50
+ const key = MESSAGE_KEYS[msg];
51
+ return key ? t(`verification.message.${key}`) : msg;
52
+ };
53
+ const checkLabel = verification.check ? t(`verification.check.${verification.check}`) : '';
54
+
31
55
  let primaryText = verification.check
32
- ? `${verification.check}: ${verification.message}`
33
- : verification.message;
56
+ ? `${checkLabel}: ${translateMessage(verification.message)}`
57
+ : translateMessage(verification.message);
34
58
  if (verification.status === VerificationStatusEnum.Failed) {
35
- primaryText = verification.message ?? verification.details ?? '';
59
+ primaryText = translateMessage(verification.message ?? verification.details ?? '');
36
60
  }
37
61
  primaryText = capitalize(primaryText);
38
62
 
@@ -45,7 +69,7 @@ const VerificationRow: React.FC<VerificationRowProps> = ({ verification }) => {
45
69
  style={{ color: statusColor }}
46
70
  >
47
71
  {getIcon()}
48
- {verification.status}
72
+ {statusText}
49
73
  {infoText && (
50
74
  <button className="ml-auto" onClick={() => setShowInfo(!showInfo)}>
51
75
  <InfoIcon color={statusColor} />
@@ -5,6 +5,7 @@ import InfoIcon from '../svgs/InfoIcon';
5
5
  import InfoBox from './InfoBox';
6
6
 
7
7
  import { VerificationItem } from '@learncard/types';
8
+ import { useT } from '../../i18n';
8
9
 
9
10
  type VerificationsBoxProps = {
10
11
  verificationItems: VerificationItem[];
@@ -12,11 +13,12 @@ type VerificationsBoxProps = {
12
13
 
13
14
  const VerificationsBox: React.FC<VerificationsBoxProps> = ({ verificationItems }) => {
14
15
  const [showInfo, setShowInfo] = useState(false);
16
+ const t = useT();
15
17
 
16
18
  return (
17
19
  <div className="verifications-box bg-white flex flex-col items-start gap-[10px] rounded-[20px] shadow-bottom px-[15px] py-[20px] w-full relative">
18
20
  <h3 className="text-[20px] leading-[20px] text-grayscale-900">
19
- Credential Verifications
21
+ {t('verification.title')}
20
22
  </h3>
21
23
  <button
22
24
  className="absolute top-[17px] right-[17px]"
@@ -28,10 +30,7 @@ const VerificationsBox: React.FC<VerificationsBoxProps> = ({ verificationItems }
28
30
  <InfoIcon color={showInfo ? '#6366F1' : undefined} />
29
31
  </button>
30
32
  {showInfo && (
31
- <InfoBox
32
- text="Credential verifications check the cryptographic proof of digital credentials to ensure their authenticity and accuracy."
33
- handleClose={() => setShowInfo(false)}
34
- />
33
+ <InfoBox text={t('verification.infoText')} handleClose={() => setShowInfo(false)} />
35
34
  )}
36
35
 
37
36
  {verificationItems.map((verification, index) => (
@@ -0,0 +1,38 @@
1
+ import type { CSSProperties } from 'react';
2
+
3
+ export type CredentialLifecycleStatus = 'active' | 'revoked' | 'suspended';
4
+
5
+ export type LifecycleTreatment = {
6
+ /** True when the credential is revoked or suspended. */
7
+ isInactive: boolean;
8
+ /** Inline filter for media (images / thumbnails); `undefined` when active.
9
+ * Inline style (not a Tailwind class) so it applies regardless of whether the
10
+ * consumer app's Tailwind scans this package or generates the utility. */
11
+ mediaStyle: CSSProperties | undefined;
12
+ /** Inline style for de-emphasizing text; `undefined` when active. */
13
+ textStyle: CSSProperties | undefined;
14
+ /** Pill background color for the status chip (only meaningful when `isInactive`). */
15
+ pillBg: string;
16
+ };
17
+
18
+ const REVOKED_COLOR = '#DC2626'; // red-600
19
+ const SUSPENDED_COLOR = '#D97706'; // amber-600
20
+
21
+ /**
22
+ * Shared visual treatment for a revoked/suspended credential, used by both the
23
+ * grid card (`BoostGenericCard`) and the list row (`BoostListItem`) so the two
24
+ * views stay in sync. Values are inert (`isInactive: false`, `undefined` styles)
25
+ * for active credentials.
26
+ */
27
+ export const getLifecycleTreatment = (
28
+ status: CredentialLifecycleStatus = 'active'
29
+ ): LifecycleTreatment => {
30
+ const isInactive = status === 'revoked' || status === 'suspended';
31
+
32
+ return {
33
+ isInactive,
34
+ mediaStyle: isInactive ? { filter: 'grayscale(1) brightness(0.9)' } : undefined,
35
+ textStyle: isInactive ? { filter: 'grayscale(1)', opacity: 0.6 } : undefined,
36
+ pillBg: status === 'suspended' ? SUSPENDED_COLOR : REVOKED_COLOR,
37
+ };
38
+ };
@@ -0,0 +1,10 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { EN_DEFAULTS } from './index';
4
+
5
+ describe('shared SDK lifecycle translations', () => {
6
+ it('provides standalone English defaults for lifecycle labels', () => {
7
+ expect(EN_DEFAULTS['credential.lifecycle.revoked']).toBe('Revoked');
8
+ expect(EN_DEFAULTS['credential.lifecycle.suspended']).toBe('Suspended');
9
+ });
10
+ });
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Lightweight i18n adapter for `@learncard/react`.
3
+ *
4
+ * This is a PUBLISHED SDK consumed by external third parties who have no
5
+ * Paraglide, no app providers, and no catalog. So the rule is: render English
6
+ * with zero configuration, and only *optionally* accept translations.
7
+ *
8
+ * Components call `useT()`. With no provider mounted, `useT()` returns the
9
+ * co-located English default (zero-config English for every consumer). A
10
+ * consumer that wants localization mounts `<I18nProvider resolve={fn} locale={l}>`
11
+ * where `fn` maps a key to a translated string using whatever i18n they use
12
+ * (Paraglide, i18next, a static map — the SDK doesn't care).
13
+ *
14
+ * Kept intentionally dependency-free and self-contained (a ~40-line copy also
15
+ * lives in learn-card-base) so the SDK pulls in no i18n runtime.
16
+ */
17
+ import React, { createContext, useCallback, useContext, useMemo } from 'react';
18
+
19
+ export type I18nResolver = (key: string, params?: Record<string, unknown>) => string | undefined;
20
+
21
+ type I18nContextValue = { resolve: I18nResolver; locale: string };
22
+
23
+ const I18nContext = createContext<I18nContextValue | null>(null);
24
+
25
+ /**
26
+ * English defaults co-located with the SDK. One entry per extracted string.
27
+ * Keys are unprefixed; a host app namespaces them (e.g. `sdk.`) in its catalog.
28
+ */
29
+ export const EN_DEFAULTS: Record<string, string> = {
30
+ 'verification.selfIssued': 'Self Issued',
31
+ 'verification.trustedIssuer': 'Trusted Issuer',
32
+ 'verification.unknownIssuer': 'Unknown Issuer',
33
+ 'verification.appIssuer': 'Trusted App',
34
+ 'verification.untrustedIssuer': 'Untrusted Issuer',
35
+ 'credential.by': 'By',
36
+ 'verification.title': 'Credential Verifications',
37
+ 'verification.infoText':
38
+ 'Credential verifications check the cryptographic proof of digital credentials to ensure their authenticity and accuracy.',
39
+ 'verification.status.success': 'Success',
40
+ 'verification.status.error': 'Error',
41
+ 'verification.status.failed': 'Failed',
42
+ 'verification.check.proof': 'Proof',
43
+ 'verification.check.status': 'Status',
44
+ 'verification.check.expiration': 'Expiration',
45
+ 'verification.message.valid': 'Valid',
46
+ 'verification.message.invalid': 'Invalid',
47
+ 'verification.message.notRevoked': 'Not Revoked',
48
+ 'verification.message.revoked': 'Revoked',
49
+ 'verification.message.suspended': 'Suspended',
50
+ 'verification.message.doesNotExpire': 'Does Not Expire',
51
+ 'verification.message.expired': 'Expired',
52
+ 'verification.message.active': 'Active',
53
+ 'verification.message.couldNotVerify': 'Boost Credential could not be verified.',
54
+ 'credential.lifecycle.revoked': 'Revoked',
55
+ 'credential.lifecycle.suspended': 'Suspended',
56
+ };
57
+
58
+ /** Minimal `{var}` interpolation — no dependency. */
59
+ const interpolate = (str: string, params?: Record<string, unknown>): string =>
60
+ params
61
+ ? str.replace(/\{(\w+)\}/g, (_, k) => (params[k] != null ? String(params[k]) : `{${k}}`))
62
+ : str;
63
+
64
+ export const I18nProvider: React.FC<{
65
+ resolve: I18nResolver;
66
+ locale: string;
67
+ children: React.ReactNode;
68
+ }> = ({ resolve, locale, children }) => {
69
+ const value = useMemo<I18nContextValue>(() => ({ resolve, locale }), [resolve, locale]);
70
+ return <I18nContext.Provider value={value}>{children}</I18nContext.Provider>;
71
+ };
72
+
73
+ /**
74
+ * Returns a translate function. Resolution order:
75
+ * 1. host resolver (consumer catalog) if a provider is mounted and has the key
76
+ * 2. co-located English default (interpolated)
77
+ * 3. the key itself (last-resort)
78
+ */
79
+ export const useT = (): ((key: string, params?: Record<string, unknown>) => string) => {
80
+ const ctx = useContext(I18nContext);
81
+ return useCallback(
82
+ (key: string, params?: Record<string, unknown>) => {
83
+ const fromHost = ctx?.resolve(key, params);
84
+ if (fromHost != null) return fromHost;
85
+ const def = EN_DEFAULTS[key];
86
+ return def != null ? interpolate(def, params) : key;
87
+ },
88
+ [ctx]
89
+ );
90
+ };
package/src/index.ts CHANGED
@@ -10,7 +10,13 @@ export * from './helpers/useScrollBorders';
10
10
  // Export DID display helpers
11
11
  export * from './helpers/did-display.helpers';
12
12
 
13
+ // Export credential lifecycle (revoked/suspended) treatment helper
14
+ export * from './helpers/lifecycle.helpers';
15
+
13
16
  // Export SD-JWT-VC hooks and helpers
14
17
  export * from './hooks/useCredentialFormat';
15
18
  export * from './hooks/useParsedSdJwtVc';
16
19
  export * from './helpers/credentialFormat.helpers';
20
+
21
+ // Optional i18n adapter (English by default; consumers may inject a resolver)
22
+ export * from './i18n';
@@ -353,6 +353,7 @@ export type BoostGenericCardProps = {
353
353
  linkedCredentialsCount?: number;
354
354
  linkedCredentialsClassName?: string;
355
355
  checkBtnClass?: string;
356
+ lifecycleStatus?: 'active' | 'revoked' | 'suspended';
356
357
  };
357
358
 
358
359
  export type Attachment = {
@@ -1 +0,0 @@
1
- {"version":3,"file":"BoostGenericCard-99a3ab28.js","sources":["../../src/components/BoostGenericCard/AlignmentSkillsCount.tsx","../../src/helpers/display.helpers.ts","../../src/components/BoostGenericCard/BoostGenericCard.tsx"],"sourcesContent":["import React from 'react';\nimport PuzzlePiece from '../svgs/PuzzlePiece';\nimport { VC } from '@learncard/types';\nimport { categorizeSkills } from '../../helpers/credential.helpers';\n\ntype AlignmentSkillsCountProps = {\n credential?: VC;\n onClick?: () => void;\n className?: string;\n};\n\nconst AlignmentSkillsCount: React.FC<AlignmentSkillsCountProps> = ({\n credential,\n onClick,\n className = '',\n}) => {\n if (!credential) return <></>;\n\n const achievement = (credential?.credentialSubject as any)?.achievement;\n let alignmentCount = achievement?.alignment?.length || 0;\n\n if (alignmentCount === 0 && credential.skills?.length > 0) {\n // fallback to legacy skills field if present and no alignments\n const skillsMap = categorizeSkills(credential.skills ?? []);\n\n // Calculate total count of skills and subskills\n const totalSkills: any = Object.values(skillsMap).reduce(\n (total: any, category: any) => total + category?.length,\n 0\n );\n const totalSubskills: any = Object.values(skillsMap).reduce(\n (total: any, category: any) => total + (category?.totalSubskillsCount || 0),\n 0\n );\n\n const total: any = totalSkills + totalSubskills;\n alignmentCount = total;\n }\n\n return (\n <div\n role={onClick ? ('button' as const) : undefined}\n onClick={onClick}\n className={`px-[10px] py-[2px] flex gap-[3px] items-center rounded-[5px] overflow-hidden bg-violet-100 ${className}`}\n >\n <PuzzlePiece className=\"w-[20px] h-[20px] text-grayscale-800\" version=\"filled\" />\n <p className=\"text-[12px] text-grayscale-800 font-poppins font-[600]\">\n {`${alignmentCount} Skill${alignmentCount !== 1 ? 's' : ''}`}\n </p>\n </div>\n );\n};\n\nexport default AlignmentSkillsCount;\n","import { LCCategoryEnum } from '../types';\n\nimport { BadgeDisplayIcon } from '../components/svgs/BadgeDisplayIcon';\nimport { CertificateDisplayIcon } from '../components/svgs/CertificateDisplayIcon';\nimport { IDDisplayIcon } from '../components/svgs/IDDisplayIcon';\nimport { CourseDisplayIcon } from '../components/svgs/CourseDisplayIcon';\nimport { AwardDisplayIcon } from '../components/svgs/AwardDisplayIcon';\n\nexport enum DisplayTypeEnum {\n Badge = 'badge',\n Certificate = 'certificate',\n ID = 'id',\n Course = 'course',\n Award = 'award',\n}\n\nexport const getDefaultDisplayType = (category: string): DisplayTypeEnum => {\n if (category === LCCategoryEnum.socialBadge || category === LCCategoryEnum.workHistory) {\n return DisplayTypeEnum.Badge;\n }\n\n if (category === LCCategoryEnum.id || category === LCCategoryEnum.membership) {\n return DisplayTypeEnum.ID;\n }\n\n if (\n category === LCCategoryEnum.achievement ||\n category === LCCategoryEnum.accommodations ||\n category === LCCategoryEnum.accomplishments ||\n category === LCCategoryEnum.learningHistory\n ) {\n return DisplayTypeEnum.Certificate;\n }\n\n return DisplayTypeEnum.Badge;\n};\n\nexport const getDisplayIcon = (displayType: DisplayTypeEnum): React.FC<{ className?: string }> => {\n switch (displayType) {\n case DisplayTypeEnum.Badge:\n return BadgeDisplayIcon;\n case DisplayTypeEnum.Certificate:\n return CertificateDisplayIcon;\n case DisplayTypeEnum.ID:\n return IDDisplayIcon;\n case DisplayTypeEnum.Course:\n return CourseDisplayIcon;\n case DisplayTypeEnum.Award:\n return AwardDisplayIcon;\n default:\n return BadgeDisplayIcon;\n }\n};\n","import React from 'react';\nimport { BoostGenericCardProps, WalletCategoryTypes } from '../../types';\nimport { TYPE_TO_IMG_SRC, TYPE_TO_WALLET_DARK_COLOR } from '../../constants';\nimport { DisplayTypeEnum, getDisplayIcon } from '../../helpers/display.helpers';\nimport AlignmentSkillsCount from './AlignmentSkillsCount';\nimport ThreeDotVertical from '../svgs/ThreeDotVertical';\nimport { CircleCheckButton } from '../CircleCheckButton';\nimport BadgeThumbnailImg from '../BadgeThumbnailImg/BadgeThumbnailImg';\n\nexport const BoostGenericCard: React.FC<BoostGenericCardProps> = ({\n title,\n customTitle,\n thumbImgSrc,\n customThumbClass = '',\n customHeaderClass = '',\n type = WalletCategoryTypes.achievements,\n className,\n onCheckClick,\n showChecked,\n checkStatus,\n customThumbComponent,\n innerOnClick,\n bgImgSrc,\n issuerName,\n customIssuerName,\n dateDisplay,\n customDateDisplay,\n optionsTriggerOnClick,\n verifierBadge,\n credential,\n isInSkillsModal,\n displayType,\n linkedCredentialsCount = 0,\n linkedCredentialsClassName = '',\n checkBtnClass = '',\n}) => {\n const thumbClass = TYPE_TO_WALLET_DARK_COLOR[type]\n ? `bg-${TYPE_TO_WALLET_DARK_COLOR[type]}`\n : 'bg-grayscale-50';\n const defaultThumbClass = `small-boost-card-thumb flex h-[110px] w-[110px] my-[10px] mx-auto ${thumbClass} overflow-hidden flex-col justify-center items-center rounded-full ${customThumbClass}`;\n const imgSrc = thumbImgSrc?.trim() !== '' ? thumbImgSrc : TYPE_TO_IMG_SRC[type];\n\n const handleInnerClick = () => innerOnClick?.();\n const handleOptionsClick = () => optionsTriggerOnClick?.();\n\n const DisplayIcon = getDisplayIcon(displayType as DisplayTypeEnum);\n\n return (\n <div\n className={`flex bg-white flex-col shadow-bottom relative p-0 w-[160px] h-[285px] rounded-[20px] overflow-hidden ${className}`}\n >\n {optionsTriggerOnClick && (\n <section\n className=\"absolute cursor-pointer h-[30px] w-[30px] top-[5px] right-[5px] rounded-full bg-white/70 flex items-center justify-center z-20\"\n onClick={handleOptionsClick}\n >\n <ThreeDotVertical className=\"h-[20px] w-[20px] text-grayscale-900\" />\n </section>\n )}\n\n {bgImgSrc && (\n <section className=\"absolute top-[-50px] left-0 rounded-b-full overflow-hidden z-0\">\n <img className=\"h-full w-full object-cover\" src={bgImgSrc} />\n </section>\n )}\n\n <button\n type=\"button\"\n className=\"z-10 flex flex-col flex-grow\"\n onClick={handleInnerClick}\n >\n {/* Thumbnail */}\n {customThumbComponent || (\n <section className={defaultThumbClass}>\n {thumbImgSrc?.trim() ? (\n <BadgeThumbnailImg\n className=\"w-full h-full rounded-full object-cover\"\n src={thumbImgSrc}\n alt=\"Credential Achievement\"\n />\n ) : (\n <img\n className=\"max-w-full p-0 object-cover rounded-full\"\n src={imgSrc}\n />\n )}\n </section>\n )}\n\n {/* Details Section: grows to fill available space */}\n <section\n className={`flex flex-col flex-grow items-center justify-end pt-1 w-full ${\n linkedCredentialsCount === 0 ? 'pb-[20px]' : ''\n }`}\n >\n <div className=\"px-1 flex flex-col items-center justify-center w-full\">\n {/* Title - dynamically size based on length */}\n {!customTitle ? (\n <p\n className={`\n ${customHeaderClass}\n font-medium text-center text-grayscale-900 line-clamp-2\n h-[50px] flex items-center justify-center\n ${\n (title?.length ?? 0) > 35\n ? 'text-[13px] leading-tight'\n : 'text-[16px]'\n }\n `}\n title={title}\n >\n {title}\n </p>\n ) : (\n customTitle\n )}\n\n {/* Verifier & Issuer */}\n <div className=\"mt-1 w-full px-4 text-center\">\n <span className=\"line-clamp-2 break-words text-[12px] leading-snug text-grayscale-700\">\n {verifierBadge && (\n <span className=\"mr-1 inline-block align-middle\">\n {verifierBadge}\n </span>\n )}\n {customIssuerName || (\n <span className=\"font-bold\">{issuerName}</span>\n )}\n </span>\n </div>\n\n {/* Date */}\n {customDateDisplay || (\n <span className=\"text-[11px] text-grayscale-700 mt-1\">\n {dateDisplay}\n </span>\n )}\n\n {/* Skills count if in modal */}\n {isInSkillsModal && credential && (\n <AlignmentSkillsCount\n credential={credential}\n onClick={handleInnerClick}\n className=\"mt-[16px]\"\n />\n )}\n </div>\n\n {/* Linked Credentials moved inside details */}\n {linkedCredentialsCount > 0 && (\n <div\n className={`mt-auto flex items-center justify-center py-1 w-full ${linkedCredentialsClassName}`}\n >\n <DisplayIcon className=\"h-[20px] w-[20px] text-grayscale-900\" />\n <span className=\"ml-1 text-sm font-semibold text-grayscale-900\">\n +{linkedCredentialsCount} Linked\n </span>\n </div>\n )}\n </section>\n </button>\n\n {/* Optional Check Button overlay */}\n {showChecked && (\n <div className=\"absolute top-[5px] left-[5px] z-20\">\n <CircleCheckButton\n checked={checkStatus}\n onClick={onCheckClick}\n className={checkBtnClass}\n />\n </div>\n )}\n </div>\n );\n};\n\nexport default BoostGenericCard;\n"],"names":["React","categorizeSkills","total","PuzzlePiece","BadgeDisplayIcon","CertificateDisplayIcon","IDDisplayIcon","CourseDisplayIcon","AwardDisplayIcon","WalletCategoryTypes","TYPE_TO_WALLET_DARK_COLOR","TYPE_TO_IMG_SRC","ThreeDotVertical","BadgeThumbnailImg","CircleCheckButton"],"mappings":"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"}