Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
88 commits
Select commit Hold shift + click to select a range
c1b9839
chore(i18n,client): processed translations (#64161)
camperbot Nov 27, 2025
7c12767
chore: remove staff from codeowners
raisedadead Nov 27, 2025
4b6c2d7
fix(api): handle string numbers in normalizeDate (#64188)
ShaunSHamilton Nov 27, 2025
b51290d
fix(curriculum): add assert to prevent empty editor pass in todo list…
Vincent-Stone Nov 27, 2025
8cbaf2b
fix(challenge-parser): add missing backtick in challenge parser fixtu…
gowtham1412-p Nov 27, 2025
15205bd
fix(a11y): update love color variables to improve label contrast in l…
pulkitbajaj Nov 28, 2025
4eeebcd
fix(curriculum): adjust wrong feedback pattern for comprehension ques…
majestic-owl448 Nov 28, 2025
fa13341
fix(curriculum): adjust feedback pattern for english curriculum task …
majestic-owl448 Nov 28, 2025
39f3a9b
fix(curriculum): correct CSS Animations quiz answer 16 wording (#64215)
DeveloperViraj Nov 28, 2025
5a703ef
fix(curriculum): corrected typo in Design Fundamentals Review (#64211)
bdtripp Nov 28, 2025
dc13404
chore(i18n,learn): update i18n-curriculum submodule (#64232)
camperbot Nov 30, 2025
26e0752
fix(curriculum): corrected formula for generating a random number (#6…
bdtripp Nov 30, 2025
176bba6
fix(curriculum): Typo in Asynchronous Programming of extra prepositio…
TheMatrix31415926 Dec 1, 2025
37ce134
refactor: (slightly) decentralize type checking (#64163)
ojeytonwilliams Dec 1, 2025
7342364
chore(i18n,client): processed translations (#64246)
camperbot Dec 1, 2025
745d463
chore(i18n,learn): update i18n-curriculum submodule (#64249)
camperbot Dec 1, 2025
e93134d
feat(curriculum): add test case for Daily Challenge - Email Validator…
pdtrang Dec 1, 2025
5d6eacb
feat(client): add pinyin-to-hanzi input to fill in the blank challeng…
huyenltnguyen Dec 1, 2025
f4494c9
fix(curriculum): specified what the descendant combinator is (#64260)
bdtripp Dec 1, 2025
fd2a32f
fix(curriculum): reversed the order of the input and label elements (…
bdtripp Dec 1, 2025
04f163f
fix(curriculum): correct float output comments in python challenges (…
HridoyMA Dec 1, 2025
d871372
fix(curriculum): remove redundant form attribute denitions in html re…
HridoyMA Dec 1, 2025
b6c2373
fix: improve explanation for descriptive link text accessibility (#64…
pulkitbajaj Dec 1, 2025
091adf3
fix(curriculum): revise sentence in Searching and Sorting Algorithms …
pdtrang Dec 1, 2025
3ea53a0
fix(curriculum): add hint for feature selection labels (#64156)
pulkitbajaj Dec 1, 2025
b55037c
feat(client): Added tests for per-module progress in client (#64226)
Almee98 Dec 1, 2025
db4aa92
chore(deps): remove jsdom from api workspace (#64204)
huyenltnguyen Dec 1, 2025
770f926
fix(curriculum): accept both multiplication orders in getRandomIndex …
ssam18 Dec 1, 2025
6a0fd9e
fix(curriculum): replace 'boolean operations' with 'boolean operators…
Rahul-2006 Dec 2, 2025
8888231
fix(client): clarify “my location” wording (#64266)
DeveloperViraj Dec 2, 2025
61f3363
fix(curriculum): fix typo of review-python-basics (#64269)
garyeung Dec 2, 2025
9c40394
fix: format + lint commands (#64256)
ojeytonwilliams Dec 2, 2025
8ef4620
chore: move challenge-auditor out of root (#64280)
ojeytonwilliams Dec 2, 2025
5699cae
fix(curriculum): move computer basics module to html (#64147)
a2937 Dec 2, 2025
96ca614
fix(curriculum): minor typo in working with loops and sequence lesson…
JayeshPatil163 Dec 2, 2025
94f2c71
feat(curriculum): update Keyboard Shortcuts lesson to include multi-l…
pdtrang Dec 2, 2025
8675090
fix(curriculum): revise feedback - Learn How to Describe Places and E…
pdtrang Dec 2, 2025
ebf5a84
refactor(client): clean up global.css (#64052)
pdtrang Dec 2, 2025
e056608
feat(client): add settings side nav (#63034)
Sembauke Dec 3, 2025
a6e5748
refactor(client): remove dataPlaywrightTestLabel prop from SectionHea…
garyeung Dec 3, 2025
b8e32be
feat(curriculum): daily challenges 120-143 (#64173)
moT01 Dec 3, 2025
4e765d0
fix(curriculum): remove redundant 'each' in Lab - Feature selection (…
Faust-Levity Dec 3, 2025
9d8ed5e
fix(tools): improve video questions validation (#64176)
gikf Dec 3, 2025
6aa1b0a
chore(i18n,client): processed translations (#64297)
camperbot Dec 3, 2025
3f7db2d
fix(curriculum): add missing feedback to Submit Events lecture MCQ (#…
huyenltnguyen Dec 3, 2025
5e5048c
chore(i18n,learn): update i18n-curriculum submodule (#64305)
camperbot Dec 3, 2025
cd07635
fix(curriculum): change spelling from travelling to to traveling (#64…
Giftea Dec 3, 2025
e73e9e2
fix(client): rm unnecessary styling to resolve the conflict in action…
garyeung Dec 3, 2025
bc52de2
fix(curriculum): moved string inverter lab (#64288)
bdtripp Dec 3, 2025
1bf90a0
fix(curriculum): change 'is agreeing' to 'agree' in Task 75 (#64304)
starboylive Dec 3, 2025
4ce039c
fix(curriculum): capitalize 'You'll' in Touch Base explaination (#64306)
Surajnairx Dec 3, 2025
9e03624
feat(client): add chapter icons for A1 Chinese and A1 Spanish chapter…
Sembauke Dec 3, 2025
b9906c3
fix(curriculum): fixed typo and added a comma in what-are-pseudo-elem…
bdtripp Dec 4, 2025
73ad631
feat(client): add pinyin tone input to fill-in-the-blank challenge (#…
huyenltnguyen Dec 4, 2025
9d8cbcf
refactor: use cert as key (#64293)
ojeytonwilliams Dec 4, 2025
c5f4795
fix: allow api to find tests for all certifications (#64295)
ojeytonwilliams Dec 4, 2025
5c568d7
fix(curriculum): fix typo in Dictionary and set review lesson (#64311)
pdtrang Dec 4, 2025
680a202
fix(client): increase chapter icon size (#64312)
AdarshPatil04 Dec 4, 2025
ff66ae8
fix(tools): allow position 0 in create-project script (#64247)
bigfatbird Dec 4, 2025
a38caec
refactor: remove certIds and the associated map (#64299)
ojeytonwilliams Dec 4, 2025
0fd9f06
feat(api): add moderation id to attempt (#64286)
ShaunSHamilton Dec 4, 2025
52d5b3c
fix(curriculum): fixed typo in CSS Pseudo-classes Review (#64310)
bdtripp Dec 4, 2025
777a06c
fix(curriculum): typo in CSS review (#64323)
bdtripp Dec 4, 2025
93816c7
fix(curriculum): minor enhancement in few code examples (#64320)
SkvProgrammer Dec 4, 2025
82a9b08
fix(curriculum): remove redundancies in a passage for HTML abbreviati…
pdtrang Dec 4, 2025
3a9c45f
fix(curriculum): remove extra spaces lab-game-character-stats (#64329)
pdtrang Dec 4, 2025
d9c81c3
fix(curriculum): typo in Python Abstraction lesson (#64332)
maxw2025 Dec 5, 2025
c0e1e13
refactor: clean up usage of certTypes (#64322)
ojeytonwilliams Dec 5, 2025
10437c3
fix(curriculum): added indentation on editable region (#64334)
alifarooqi Dec 5, 2025
dd2a6ed
fix(Curriculum): Remove "write the letter" from sentence in A1 Spanis…
nieldakarla Dec 5, 2025
89a8bd2
fix(curriculum): typo in DC 142 seed code (#64331)
moT01 Dec 5, 2025
bcacf74
fix(client): hide console on JS-only interactive examples (#64210)
bengguankoay Dec 5, 2025
29420e8
chore: remove validate-keys script (#64341)
ojeytonwilliams Dec 5, 2025
6949c96
fix(curriculum): update email simulator tests (#63803)
Dario-DC Dec 5, 2025
0a8f7a6
feat(curriculum): update A1 professional Spanish description (#64344)
estefaniacn Dec 5, 2025
b664cdb
chore(i18n,learn): update i18n-curriculum submodule (#64346)
camperbot Dec 5, 2025
a28ff5d
fix(client): correct placement of Interactive Editor button (#64350)
pdtrang Dec 5, 2025
39eb3e0
fix(client): add beta back to a2-english (#64368)
ShaunSHamilton Dec 8, 2025
cabddb7
feat(challenge-parser,client): display Chinese dialogue with ruby ann…
huyenltnguyen Dec 8, 2025
a09b430
fix: Typos in lecture on Working with CSS Transforms, Overflow, and F…
bdtripp Dec 8, 2025
6a54b7c
fix(client): change twitter to X and update twitter URL's (#64046)
moT01 Dec 8, 2025
95ad3c9
fix: typo in What Are Common Issues When Styling Special Input Elemen…
bdtripp Dec 8, 2025
7e067ff
fix(curriculum): typo in Game Settings Panel workshop (#64356)
bdtripp Dec 8, 2025
2fee689
chore(i18n,client): processed translations (#64371)
camperbot Dec 8, 2025
ff83419
fix(curriculum): update examples in lecture-working-with-loops-and-se…
pdtrang Dec 8, 2025
815b9bb
fix(curriculum): update text in lecture-working-with-loops-and-sequen…
pdtrang Dec 8, 2025
75098c3
fix(curriculum): move checkbox input before label text (#64376)
DeveloperViraj Dec 8, 2025
786f840
fix(curriculum): typo in react basics review (#64386)
bdtripp Dec 8, 2025
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
8 changes: 3 additions & 5 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,12 @@ pnpm-lock.yaml
/curriculum/challenges/english/* @freecodecamp/curriculum

# -------------------------------------------------
# Files that need attention from Staff
# Files that need attention from i18n & dev team
# -------------------------------------------------

# README, LICENSE, etc.
/*.md @freeCodeCamp/staff

# i18n Quotes
**/motivation.json @freeCodeCamp/staff @freeCodeCamp/i18n
**/motivation.json @freeCodeCamp/dev-team @freeCodeCamp/i18n


# -------------------------------------------------
# Files that need attention from the mobile team
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/node.js-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ jobs:
- name: Lint Source Files
run: |
echo pnpm version $(pnpm -v)
pnpm turbo lint
pnpm lint
# DONT REMOVE THIS JOB.
# TODO: Refactor and use re-usable workflow and shared artifacts
Expand Down
1 change: 1 addition & 0 deletions api/__mocks__/exam-environment-exam.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ export const generatedExam: ExamEnvironmentGeneratedExam = {
export const examAttempt: ExamEnvironmentExamAttempt = {
examId,
generatedExamId: generatedExam.id,
examModerationId: null,
id: oid(),
questionSets: [
{
Expand Down
1 change: 0 additions & 1 deletion api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@
"dotenv-cli": "7.3.0",
"eslint": "^9.39.1",
"eslint-plugin-jsdoc": "48.2.1",
"jsdom": "^26.1.0",
"msw": "^2.7.0",
"prisma": "6.16.2",
"supertest": "6.3.3",
Expand Down
23 changes: 14 additions & 9 deletions api/prisma/exam-environment.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -118,26 +118,31 @@ type ExamEnvironmentTagConfig {

/// An attempt at an exam in the Exam Environment App
model ExamEnvironmentExamAttempt {
id String @id @default(auto()) @map("_id") @db.ObjectId
id String @id @default(auto()) @map("_id") @db.ObjectId
/// Foriegn key to user
userId String @db.ObjectId
userId String @db.ObjectId
/// Foreign key to exam
examId String @db.ObjectId
examId String @db.ObjectId
/// Foreign key to generated exam id
generatedExamId String @db.ObjectId
generatedExamId String @db.ObjectId
/// Un-enforced foreign key to moderation
examModerationId String? @db.ObjectId

questionSets ExamEnvironmentQuestionSetAttempt[]
/// Time exam was started
startTime DateTime
/// Version of the record
/// The default must be incremented by 1, if anything in the schema changes
version Int @default(3)
version Int @default(4)

// Relations
user user @relation(fields: [userId], references: [id], onDelete: Cascade)
exam ExamEnvironmentExam @relation(fields: [examId], references: [id], onDelete: Cascade)
generatedExam ExamEnvironmentGeneratedExam @relation(fields: [generatedExamId], references: [id])
ExamEnvironmentExamModeration ExamEnvironmentExamModeration[]
user user @relation(fields: [userId], references: [id], onDelete: Cascade)
exam ExamEnvironmentExam @relation(fields: [examId], references: [id], onDelete: Cascade)
generatedExam ExamEnvironmentGeneratedExam @relation(fields: [generatedExamId], references: [id])
// Ideally, there could be a way to add a one-way optional relation here, but Prisma does not allow that:
// Error parsing attribute "@relation": The relation fields `examAttempt` on Model `ExamEnvironmentExamModeration` and `examModeration` on Model `ExamEnvironmentExamAttempt` both provide the `references` argument in the @relation attribute. You have to provide it only on one of the two fields.
// examModeration ExamEnvironmentExamModeration? @relation(fields: [examModerationId], references: [id])
examEnvironmentExamModeration ExamEnvironmentExamModeration?
}

type ExamEnvironmentQuestionSetAttempt {
Expand Down
1 change: 1 addition & 0 deletions api/src/exam-environment/routes/exam-environment.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,7 @@ describe('/exam-environment/', () => {
userId: defaultUserId,
examId: mock.examId,
generatedExamId: generatedExam!.id,
examModerationId: null,
questionSets: [],
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
startTime: expect.any(Date),
Expand Down
1 change: 1 addition & 0 deletions api/src/exam-environment/routes/exam-environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,7 @@ async function postExamGeneratedExamHandler(
userId: user.id,
examId: exam.id,
generatedExamId: generatedExam.id,
examModerationId: null,
startTime: new Date(),
questionSets: []
}
Expand Down
1 change: 1 addition & 0 deletions api/src/exam-environment/utils/exam-environment.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,7 @@ describe('Exam Environment Schema', () => {
data: {
examId: oid(),
generatedExamId: oid(),
examModerationId: null,
questionSets: [
{
id: oid(),
Expand Down
24 changes: 8 additions & 16 deletions api/src/routes/helpers/certificate-utils.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,18 @@
import { Prisma } from '@prisma/client';
import {
certSlugTypeMap,
certIds
certToIdMap,
Certification
} from '../../../../shared/config/certification-settings.js';
import { normalizeDate } from '../../utils/normalize.js';

const {
legacyInfosecQaId,
respWebDesignId,
frontEndDevLibsId,
jsAlgoDataStructId,
dataVis2018Id,
apisMicroservicesId
} = certIds;

const fullStackCertificateIds = [
respWebDesignId,
jsAlgoDataStructId,
frontEndDevLibsId,
dataVis2018Id,
apisMicroservicesId,
legacyInfosecQaId
certToIdMap[Certification.RespWebDesign],
certToIdMap[Certification.JsAlgoDataStruct],
certToIdMap[Certification.FrontEndDevLibs],
certToIdMap[Certification.DataVis],
certToIdMap[Certification.BackEndDevApis],
certToIdMap[Certification.LegacyInfoSecQa]
];

/**
Expand Down
31 changes: 31 additions & 0 deletions api/src/routes/protected/certificate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import {
setupServer,
superRequest
} from '../../../vitest.utils.js';
import { getChallenges } from '../../utils/get-challenges.js';
import { createCertLookup } from './certificate.js';

describe('certificate routes', () => {
setupServer();
Expand Down Expand Up @@ -461,3 +463,32 @@ describe('certificate routes', () => {
});
});
});

describe('createCertLookup', () => {
let challenges: ReturnType<typeof getChallenges>;

beforeAll(() => {
// TODO: create a mock challenges array specific to these tests.
challenges = getChallenges();
});

test('should create a lookup for all certifications', () => {
const certLookup = createCertLookup(challenges);

for (const cert of Object.values(Certification)) {
const certData = certLookup[cert];
expect(certData).toHaveProperty('id');
expect(certData).toHaveProperty('tests');
expect(certData).toHaveProperty('challengeType');
}
});

test('each certification should have a unique challenge id', () => {
const certLookup = createCertLookup(challenges);
const ids = Object.values(certLookup)
.map(({ id }) => id)
.sort();
const uniqueIds = Array.from(new Set(ids)).sort();
expect(uniqueIds).toEqual(ids);
});
});
109 changes: 31 additions & 78 deletions api/src/routes/protected/certificate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import type { FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebo

import { getChallenges } from '../../utils/get-challenges.js';
import {
certIds,
Certification,
certSlugTypeMap,
certTypeTitleMap,
certTypes,
certToIdMap,
certToTitleMap,
currentCertifications,
legacyCertifications,
legacyFullStackCertification,
Expand All @@ -20,31 +20,6 @@ import { normalizeChallenges, removeNulls } from '../../utils/normalize.js';
import { SHOW_UPCOMING_CHANGES } from '../../utils/env.js';
import { isKnownCertSlug } from '../helpers/certificate-utils.js';

const {
a2EnglishId,
legacyFrontEndChallengeId,
legacyBackEndChallengeId,
legacyDataVisId,
legacyInfosecQaId,
legacyFullStackId,
respWebDesignId,
frontEndDevLibsId,
javascriptV9Id,
jsAlgoDataStructId,
jsAlgoDataStructV8Id,
dataVis2018Id,
apisMicroservicesId,
qaV7Id,
infosecV7Id,
sciCompPyV7Id,
dataAnalysisPyV7Id,
machineLearningPyV7Id,
relationalDatabaseV8Id,
respWebDesignV9Id,
collegeAlgebraPyV8Id,
foundationalCSharpV8Id
} = certIds;

function isCertAllowed(certSlug: string): boolean {
if (
currentCertifications.includes(certSlug) ||
Expand Down Expand Up @@ -108,10 +83,11 @@ function assertTestsExist(
}
}

function getCertById(
challengeId: string,
function getCertBySlug(
cert: Certification,
challenges: ReturnType<typeof getChallenges>
): { id: string; tests: { id: string }[]; challengeType: number } {
const challengeId = certToIdMap[cert];
const challengeById = challenges.filter(({ id }) => id === challengeId)[0];
if (!challengeById) {
throw new Error(`Challenge with id '${challengeId}' not found`);
Expand All @@ -121,51 +97,28 @@ function getCertById(
return { id, tests, challengeType };
}

function createCertTypeIds(challenges: ReturnType<typeof getChallenges>) {
return {
// legacy
[certTypes.frontEnd]: getCertById(legacyFrontEndChallengeId, challenges),
[certTypes.jsAlgoDataStruct]: getCertById(jsAlgoDataStructId, challenges),
[certTypes.backEnd]: getCertById(legacyBackEndChallengeId, challenges),
[certTypes.dataVis]: getCertById(legacyDataVisId, challenges),
[certTypes.infosecQa]: getCertById(legacyInfosecQaId, challenges),
[certTypes.fullStack]: getCertById(legacyFullStackId, challenges),

// modern
[certTypes.respWebDesign]: getCertById(respWebDesignId, challenges),
[certTypes.frontEndDevLibs]: getCertById(frontEndDevLibsId, challenges),
[certTypes.dataVis2018]: getCertById(dataVis2018Id, challenges),
[certTypes.jsAlgoDataStructV8]: getCertById(
jsAlgoDataStructV8Id,
challenges
),
[certTypes.apisMicroservices]: getCertById(apisMicroservicesId, challenges),
[certTypes.qaV7]: getCertById(qaV7Id, challenges),
[certTypes.infosecV7]: getCertById(infosecV7Id, challenges),
[certTypes.sciCompPyV7]: getCertById(sciCompPyV7Id, challenges),
[certTypes.dataAnalysisPyV7]: getCertById(dataAnalysisPyV7Id, challenges),
[certTypes.machineLearningPyV7]: getCertById(
machineLearningPyV7Id,
challenges
),
[certTypes.relationalDatabaseV8]: getCertById(
relationalDatabaseV8Id,
challenges
),
[certTypes.collegeAlgebraPyV8]: getCertById(
collegeAlgebraPyV8Id,
challenges
),
[certTypes.foundationalCSharpV8]: getCertById(
foundationalCSharpV8Id,
challenges
),
[certTypes.javascriptV9]: getCertById(javascriptV9Id, challenges),
[certTypes.respWebDesignV9]: getCertById(respWebDesignV9Id, challenges),

// upcoming
[certTypes.a2English]: getCertById(a2EnglishId, challenges)
};
type CertLookup = Record<
Certification,
{ id: string; tests: { id: string }[]; challengeType: number }
>;

/**
* Create a lookup from Certification enum values to their corresponding
* challenge metadata (id, tests and challengeType) using the provided
* challenges array.
*
* @param challenges - The array returned by getChallenges().
* @returns A record mapping each Certification to an object with id, tests and challengeType.
*/
export function createCertLookup(
challenges: ReturnType<typeof getChallenges>
): CertLookup {
const certLookup = {} as CertLookup;

for (const cert of Object.values(Certification)) {
certLookup[cert] = getCertBySlug(cert, challenges);
}
return certLookup;
}

interface CertI {
Expand Down Expand Up @@ -258,7 +211,7 @@ export const protectedCertificateRoutes: FastifyPluginCallbackTypebox = (
done
) => {
const challenges = getChallenges();
const certTypeIds = createCertTypeIds(challenges);
const certLookup = createCertLookup(challenges);

// TODO(POST_MVP): Response should not include updated user. If a client wants the updated user, it should make a separate request
// OR: Always respond with current user - full user object - not random pieces.
Expand Down Expand Up @@ -298,7 +251,7 @@ export const protectedCertificateRoutes: FastifyPluginCallbackTypebox = (
}

const certType = certSlugTypeMap[certSlug];
const certName = certTypeTitleMap[certType];
const certName = certToTitleMap[certSlug];

const user = await fastify.prisma.user.findUnique({
where: { id: req.user?.id }
Expand Down Expand Up @@ -347,7 +300,7 @@ export const protectedCertificateRoutes: FastifyPluginCallbackTypebox = (
} as const;
}

const { id, tests, challengeType } = certTypeIds[certType];
const { id, tests, challengeType } = certLookup[certSlug];
const hasCompletedTestRequirements = hasCompletedTests(
tests,
user.completedChallenges
Expand Down
2 changes: 1 addition & 1 deletion api/src/routes/protected/user.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ const publicUserData = {
portfolio: testUserData.portfolio,
profileUI: testUserData.profileUI,
savedChallenges: testUserData.savedChallenges,
twitter: 'https://twitter.com/foobar',
twitter: 'https://x.com/foobar',
bluesky: 'https://bsky.app/profile/foobar',
sendQuincyEmail: testUserData.sendQuincyEmail,
username: testUserData.username,
Expand Down
15 changes: 7 additions & 8 deletions api/src/routes/public/certificate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { find } from 'lodash-es';
import * as schemas from '../../schemas.js';
import {
certSlugTypeMap,
certTypeTitleMap,
certTypeIdMap,
certToTitleMap,
certToIdMap,
completionHours,
oldDataVizId
} from '../../../../shared/config/certification-settings.js';
Expand Down Expand Up @@ -52,9 +52,9 @@ export const unprotectedCertificateRoutes: FastifyPluginCallbackTypebox = (
}

const certType = certSlugTypeMap[certSlug];
const certId = certTypeIdMap[certType];
const certTitle = certTypeTitleMap[certType];
const completionTime = completionHours[certType] || 300;
const certId = certToIdMap[certSlug];
const certTitle = certToTitleMap[certSlug];
const completionTime = completionHours[certSlug] || 300;
const user = await fastify.prisma.user.findFirst({
where: { username },
select: {
Expand Down Expand Up @@ -180,16 +180,15 @@ export const unprotectedCertificateRoutes: FastifyPluginCallbackTypebox = (
}

if (!user[certType]) {
const cert = certTypeTitleMap[certType];
logger.info(
`User ${username} has not completed the ${cert} certification.`
`User ${username} has not completed the ${certTitle} certification.`
);
return reply.send({
messages: [
{
type: 'info',
message: 'flash.user-not-certified',
variables: { username, cert }
variables: { username, cert: certTitle }
}
]
});
Expand Down
Loading
Loading