Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/upset-mugs-appear.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@learncard/vc-templates-plugin': patch
'@learncard/react': patch
---

Properly handle Obv3 alignments field
16 changes: 10 additions & 6 deletions packages/plugins/vc-templates/src/templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ export const VC_TEMPLATES: {
display,
familyTitles,
skills,
alignments,
groupID = '',
} = {},
crypto
Expand Down Expand Up @@ -146,6 +147,7 @@ export const VC_TEMPLATES: {
criteria: {
narrative: achievementNarrative,
},
...(alignments && alignments.length ? { alignments } : {}),
},
},
display,
Expand All @@ -172,6 +174,7 @@ export const VC_TEMPLATES: {
address,
attachments,
skills,
alignments,
display,
familyTitles,
boostID,
Expand Down Expand Up @@ -204,16 +207,17 @@ export const VC_TEMPLATES: {
criteria: {
narrative: achievementNarrative,
},
...(alignments && alignments.length ? { alignments } : {}),
},
},
...(address
? {
address: {
type: ['Address'],
...address,
...(address.geo ? { geo: { type: ['GeoCoordinates'], ...address.geo } } : {}),
},
}
address: {
type: ['Address'],
...address,
...(address.geo ? { geo: { type: ['GeoCoordinates'], ...address.geo } } : {}),
},
}
: {}),
display,
familyTitles,
Expand Down
4 changes: 3 additions & 1 deletion packages/plugins/vc-templates/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { UnsignedVC, VC, UnsignedVP } from '@learncard/types';
import { UnsignedVC, VC, UnsignedVP, Alignment } from '@learncard/types';
import { DiscriminatedUnionize } from './type.helpers';
import { Plugin } from '@learncard/core';

Expand Down Expand Up @@ -90,6 +90,8 @@ export type BoostTemplate = {
achievementImage?: string;
attachments?: BoostAttachment[];
skills?: BoostSkills[];
/** OBv3 alignment targets associated with the achievement */
alignments?: Alignment[];
display?: BoostDisplay;
familyTitles?: BoostFamilyTitles;
boostID?: BoostID;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,20 @@
import React from 'react';
import CaretRightFilled from '../../assets/images/CaretRightFilled.svg';

type AlignmentsRowProps = {
url?: string
name?: string
framework?: string
type AlignmentRowProps = {
url?: string;
name?: string;
framework?: string;
};
const AlignmentRow:React.FC<AlignmentsRowProps> = ({
url,
name,
framework
}) => {
const AlignmentRow: React.FC<AlignmentRowProps> = ({ url, name, framework }) => {
return (
<div className="flex flex-col gap-[5px] font-poppins text-[12px] bg-[#DBEAFE] rounded-[15px] border-b-[1px] border-grayscale-200 border-solid w-full p-[10px] last:border-0">
<h1 className="text-blue-800 font-semibold uppercase">{framework}</h1>
{/* this might need to change to a link depends on how it comes back after the api call */}
<button className="flex" onClick={() => window.open(url)}>
<span className="text-left">{name}</span>
<img className="w-[20px] self-end" src={CaretRightFilled} alt="right-caret"/>
</button>
<h1 className="text-blue-800 font-semibold uppercase">{framework}</h1>
{/* this might need to change to a link depends on how it comes back after the api call */}
<button className="flex" onClick={() => window.open(url)}>
<span className="text-left">{name}</span>
<img className="w-[20px] self-end" src={CaretRightFilled} alt="right-caret" />
</button>
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ type Alignment = {
targetUrl: string;
targetName: string;
targetFramework: string;
}
};

type AlignmentsBoxProps = {
alignment: Alignment | Alignment[];
style: 'Certificate' | 'boost';
style: 'Certificate' | 'boost';
};

const AlignmentsBox:React.FC<AlignmentsBoxProps> = ({ alignment, style }) => {
const AlignmentsBox: React.FC<AlignmentsBoxProps> = ({ alignment, style }) => {
const [showInfo, setShowInfo] = useState(false);
const alignmentText = `
Alignments in your Open Badge credential link your achievement to established frameworks, standards, or competencies.
Expand All @@ -42,17 +42,20 @@ const AlignmentsBox:React.FC<AlignmentsBoxProps> = ({ alignment, style }) => {
return (
<div className="bg-white flex flex-col items-start gap-[10px] rounded-[20px] shadow-bottom p-[15px] w-full">
<div className="flex w-full items-center">
<h3 className={style === "Certificate" ? "text-[17px] text-grayscale-900 font-poppins" : "text-[22px] font-mouse"}>Alignments</h3>
<h3
className={
style === 'Certificate'
? 'text-[17px] text-grayscale-900 font-poppins'
: 'text-[22px] font-mouse'
}
>
Alignments
</h3>
<button className="ml-auto" onClick={() => setShowInfo(!showInfo)}>
<InfoIcon color={showInfo ? '#6366F1' : undefined} />
</button>
</div>
{showInfo && (
<InfoBox
text={alignmentText}
handleClose={() => setShowInfo(false)}
/>
)}
{showInfo && <InfoBox text={alignmentText} handleClose={() => setShowInfo(false)} />}
{alignments}
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export const CertificateBackFace: React.FC<CertificateBackFaceProps> = ({

const { description } = credentialSubject?.achievement ?? {};
const criteria = credentialSubject?.achievement?.criteria?.narrative;
const alignment = credentialSubject?.achievement?.alignment;
const alignments = credentialSubject?.achievement?.alignments;

const credentialDarkColor = getCategoryDarkColor(categoryType);

Expand Down Expand Up @@ -115,7 +115,7 @@ export const CertificateBackFace: React.FC<CertificateBackFaceProps> = ({
/>
)}

{alignment && <AlignmentsBox alignment={alignment} style="Certificate" />}
{alignments && <AlignmentsBox alignment={alignments} style="Certificate" />}

{verificationItems && verificationItems.length > 0 && (
<VerificationsBox verificationItems={verificationItems} />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import React from 'react';
import { Story, Meta } from '@storybook/react';
import CertificateDisplayCard from './CertificateDisplayCard';
import { AllFieldsCredential } from '../../helpers/test.helpers';
import { TestVerificationItems } from '../../helpers/test.helpers';

export default {
title: 'CertificateDisplayCard',
Expand All @@ -12,4 +14,16 @@ export default {
const Template: Story = args => <CertificateDisplayCard {...args} />;

export const CertificateDisplayCardTest = Template.bind({});
CertificateDisplayCardTest.args = {};
CertificateDisplayCardTest.args = {
credential: AllFieldsCredential,
verificationItems: [
TestVerificationItems.SUCCESS.PROOF,
TestVerificationItems.SUCCESS.NO_EXPIRATION,
TestVerificationItems.SUCCESS.NOT_EXPIRED,
TestVerificationItems.FAILED.EXPIRED,
TestVerificationItems.FAILED.CONTEXT,
TestVerificationItems.FAILED.SIGNATURE,
TestVerificationItems.FAILED.PROOF_TYPE,
TestVerificationItems.FAILED.APPLICABLE_PROOF,
],
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,19 @@ import React from 'react';
import CaretRightFilled from '../../assets/images/CaretRightFilled.svg';

type AlignmentsRowProps = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧾 Readability - Inconsistent Naming: Rename the type from AlignmentsRowProps to AlignmentRowProps to maintain consistency with the component name and match the pattern used in the CertificateDisplayCard version.

Suggested change
type AlignmentsRowProps = {
type AlignmentRowProps = {

url?: string
name?: string
framework?: string
url?: string;
name?: string;
framework?: string;
};
const AlignmentRow:React.FC<AlignmentsRowProps> = ({
url,
name,
framework
}) => {
const AlignmentRow: React.FC<AlignmentsRowProps> = ({ url, name, framework }) => {
return (
<div className="flex flex-col gap-[5px] font-poppins text-[12px] bg-[#DBEAFE] rounded-[15px] border-b-[1px] border-grayscale-200 border-solid w-full p-[10px] last:border-0">
<h1 className="text-blue-800 font-semibold uppercase">{framework}</h1>
{/* this might need to change to a link depends on how it comes back after the api call */}
<button className="flex" onClick={() => window.open(url)}>
<span className="text-left">{name}</span>
<img className="w-[20px] self-end" src={CaretRightFilled} alt="right-caret"/>
</button>
<h1 className="text-blue-800 font-semibold uppercase">{framework}</h1>
{/* this might need to change to a link depends on how it comes back after the api call */}
<button className="flex" onClick={() => window.open(url)}>
<span className="text-left">{name}</span>
<img className="w-[20px] self-end" src={CaretRightFilled} alt="right-caret" />
</button>
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,20 @@ const AlignmentsBox: React.FC<AlignmentsBoxProps> = ({ alignment, style }) => {

const alignments = Array.isArray(alignment)
? alignment.map((object, index) => (
<AlignmentRow
key={index}
url={object.targetUrl}
name={object.targetName}
framework={object.targetFramework}
/>
))
<AlignmentRow
key={index}
url={object.targetUrl}
name={object.targetName}
framework={object.targetFramework}
/>
))
: alignment && (
<AlignmentRow
url={alignment.targetUrl}
name={alignment.targetName}
framework={alignment.targetFramework}
/>
);
<AlignmentRow
url={alignment.targetUrl}
name={alignment.targetName}
framework={alignment.targetFramework}
/>
);

return (
<div className="bg-white flex flex-col items-start gap-[10px] rounded-[20px] shadow-bottom p-[15px] w-full">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export const MeritBadgeBackFace: React.FC<MeritBadgeBackFaceProps> = ({

const { description } = credentialSubject?.achievement ?? {};
const criteria = credentialSubject?.achievement?.criteria?.narrative;
const alignment = credentialSubject?.achievement?.alignment;
const alignments = credentialSubject?.achievement?.alignments;

const credentialDarkColor = getCategoryDarkColor(categoryType);

Expand Down Expand Up @@ -115,7 +115,7 @@ export const MeritBadgeBackFace: React.FC<MeritBadgeBackFaceProps> = ({
/>
)}

{alignment && <AlignmentsBox alignment={alignment} style="MeritBadge" />}
{alignments && <AlignmentsBox alignment={alignments} style="MeritBadge" />}

{verificationItems && verificationItems.length > 0 && (
<VerificationsBox verificationItems={verificationItems} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ const VC2BackFace: React.FC<VC2BackFaceProps> = ({
: undefined;
const criteria = achievement?.criteria?.narrative;
const description = achievement?.description;
const alignment = achievement?.alignment;
const alignments = achievement?.alignments;

/*
const tags = credential.credentialSubject.achievement?.tag;
Expand Down Expand Up @@ -164,7 +164,7 @@ const VC2BackFace: React.FC<VC2BackFaceProps> = ({
)}
{/* {credential.notes && <TruncateTextBox headerText="Notes" text={credential.notes} />} */}

{alignment && <AlignmentsBox alignment={alignment} style="boost" />}
{alignments && <AlignmentsBox alignment={alignments} style="boost" />}

{verificationItems && verificationItems.length > 0 && (
<VerificationsBox verificationItems={verificationItems} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,25 @@ export const ThreeDotVertical = ({ className }: { className?: string }) => {
d="M8.4375 10C8.4375 10.8629 9.13706 11.5625 10 11.5625C10.8629 11.5625 11.5625 10.8629 11.5625 10C11.5625 9.13706 10.8629 8.4375 10 8.4375C9.13706 8.4375 8.4375 9.13705 8.4375 10Z"
fill="#6F7590"
stroke="#6F7590"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M8.4375 16.25C8.4375 17.1129 9.13706 17.8125 10 17.8125C10.8629 17.8125 11.5625 17.1129 11.5625 16.25C11.5625 15.3871 10.8629 14.6875 10 14.6875C9.13706 14.6875 8.4375 15.3871 8.4375 16.25Z"
fill="#6F7590"
stroke="#6F7590"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M8.4375 3.75C8.4375 4.61294 9.13706 5.3125 10 5.3125C10.8629 5.3125 11.5625 4.61294 11.5625 3.75C11.5625 2.88706 10.8629 2.1875 10 2.1875C9.13706 2.1875 8.4375 2.88705 8.4375 3.75Z"
fill="#6F7590"
stroke="#6F7590"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
Expand Down
20 changes: 20 additions & 0 deletions packages/react-learn-card/src/helpers/test.helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,26 @@ export const AllFieldsCredential = {
narrative:
'You really know your crednetials! Pretty rad. Being able to find and handle all of these fields is incredible! This narrative is going to be pretty long so that we can see if you can handle long blocks of text too. This should probably be truncated off by now or something.',
},
alignments: [
{
type: 'Alignment',
targetType: 'Competency',
targetName: 'Teamwork',
targetDescription: 'Teamwork is the ability to work well with others',
targetFramework: '21st Century Skills',
targetUrl: 'https://example.com/alignments/21st-century-skills/teamwork',
targetCode: '123',
},
{
type: ['Alignment'],
targetType: 'Competency',
targetName: 'Quick Thinker',
targetDescription: 'Quick Thinker is the ability to think quickly',
targetFramework: '21st Century Skills',
targetUrl: 'https://example.com/alignments/21st-century-skills/quick-thinker',
targetCode: '456',
},
],
},
},
proof: {
Expand Down
Loading