chore: heygermany customer project
This commit is contained in:
225
packages/functions/src/engines/candidate-text-engine.ts
Normal file
225
packages/functions/src/engines/candidate-text-engine.ts
Normal file
@@ -0,0 +1,225 @@
|
||||
import type { DB, GetCandidateResult } from '@heygermany/sdk'
|
||||
import {
|
||||
DocumentStatuses,
|
||||
LanguageLevels,
|
||||
NurseRecognitionGermanyValues,
|
||||
} from '@heygermany/sdk'
|
||||
import type { TranslationService } from "../services/translation-service.js"
|
||||
|
||||
export const createCandidateTextEngine = (translation: TranslationService, locale: string) => ({
|
||||
version: 1,
|
||||
|
||||
// ============= RULES (inputs) =============
|
||||
rules: [
|
||||
// Nurse — all met
|
||||
{
|
||||
id: "nurse_allMet",
|
||||
when: {
|
||||
and: [
|
||||
{ "==": [{ var: "qualificationStatusGermany" }, NurseRecognitionGermanyValues.NURSE] },
|
||||
{ "==": [{ var: "nursingRelatedDegree" }, true] },
|
||||
{ "==": [{ var: "structuredTraining.overallStatus" }, true] },
|
||||
{ "==": [{ var: "accreditedUniversity" }, true] },
|
||||
{ "or": [
|
||||
{ "==": [{ var: "licenseMandatory" }, false] },
|
||||
{ "==": [{ var: "docs.license" }, DocumentStatuses.VERIFIED] }
|
||||
]}
|
||||
]
|
||||
},
|
||||
render: "nurse.allMet"
|
||||
},
|
||||
|
||||
// Nurse — missing license (accreditation OK)
|
||||
{
|
||||
id: "nurse_missingLicense",
|
||||
when: {
|
||||
and: [
|
||||
{ "==": [{ var: "qualificationStatusGermany" }, NurseRecognitionGermanyValues.NURSE] },
|
||||
{ "==": [{ var: "licenseMandatory" }, true] },
|
||||
{ "!=": [{ var: "docs.license" }, DocumentStatuses.VERIFIED] },
|
||||
{ "==": [{ var: "accreditedUniversity" }, true] }
|
||||
]
|
||||
},
|
||||
render: "nurse.missingLicense"
|
||||
},
|
||||
|
||||
// Nurse — missing license + accreditation
|
||||
{
|
||||
id: "nurse_missingLicenseAndAccreditation",
|
||||
when: {
|
||||
and: [
|
||||
{ "==": [{ var: "qualificationStatusGermany" }, NurseRecognitionGermanyValues.NURSE] },
|
||||
{ "==": [{ var: "licenseMandatory" }, true] },
|
||||
{ "!=": [{ var: "docs.license" }, DocumentStatuses.VERIFIED] },
|
||||
{ "==": [{ var: "accreditedUniversity" }, false] }
|
||||
]
|
||||
},
|
||||
render: "nurse.missingLicenseAndAccreditation"
|
||||
},
|
||||
|
||||
// Assistant Nurse — all met
|
||||
{
|
||||
id: "assistant_allMet",
|
||||
when: {
|
||||
and: [
|
||||
{ "==": [{ var: "qualificationStatusGermany" }, NurseRecognitionGermanyValues.ASSISTANT_NURSE] },
|
||||
{ "or": [
|
||||
{ "==": [{ var: "licenseMandatory" }, false] },
|
||||
{ "==": [{ var: "docs.license" }, DocumentStatuses.VERIFIED] }
|
||||
]},
|
||||
{ "==": [{ var: "nursingRelatedDegree" }, true] },
|
||||
{ "==": [{ var: "structuredTraining.overallStatus" }, true] },
|
||||
{ "==": [{ var: "accreditedUniversity" }, true] }
|
||||
]
|
||||
},
|
||||
render: "assistant.allMet"
|
||||
},
|
||||
|
||||
// Assistant Nurse — missing license
|
||||
{
|
||||
id: "assistant_missingLicense",
|
||||
when: {
|
||||
and: [
|
||||
{ "==": [{ var: "qualificationStatusGermany" }, NurseRecognitionGermanyValues.ASSISTANT_NURSE] },
|
||||
{ "==": [{ var: "licenseMandatory" }, true] },
|
||||
{ "!=": [{ var: "docs.license" }, DocumentStatuses.VERIFIED] },
|
||||
{ "==": [{ var: "accreditedUniversity" }, true] }
|
||||
]
|
||||
},
|
||||
render: "assistant.missingLicense"
|
||||
},
|
||||
|
||||
// Assistant Nurse — missing license + accreditation
|
||||
{
|
||||
id: "assistant_missingLicenseAndAccreditation",
|
||||
when: {
|
||||
and: [
|
||||
{ "==": [{ var: "qualificationStatusGermany" }, NurseRecognitionGermanyValues.ASSISTANT_NURSE] },
|
||||
{ "==": [{ var: "licenseMandatory" }, true] },
|
||||
{ "!=": [{ var: "docs.license" }, DocumentStatuses.VERIFIED] },
|
||||
{ "==": [{ var: "accreditedUniversity" }, false] }
|
||||
]
|
||||
},
|
||||
render: "assistant.missingLicenseAndAccreditation"
|
||||
},
|
||||
|
||||
// Nursing Helper — default
|
||||
{
|
||||
id: "helper_default",
|
||||
when: { "==": [{ var: "qualificationStatusGermany" }, NurseRecognitionGermanyValues.NURSING_HELPER] },
|
||||
render: "helper.default"
|
||||
}
|
||||
],
|
||||
|
||||
// ============= GLOBAL NOTES/ADD-ONS (inputs) =============
|
||||
notes: [
|
||||
{
|
||||
id: "visa_note",
|
||||
when: {
|
||||
and: [
|
||||
{ "==": [{ var: "euPassport" }, false] },
|
||||
{ "==": [{ var: "livingInGermany" }, false] },
|
||||
{
|
||||
and: [
|
||||
{ "!=": [{ var: "languageLevel" }, LanguageLevels.B_TWO] },
|
||||
{ "!=": [{ var: "languageLevel" }, LanguageLevels.C_ONE] },
|
||||
{ "!=": [{ var: "languageLevel" }, LanguageLevels.C_TWO] }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
textKey: "notes.visanote",
|
||||
attach: { where: "afterExplanation" } // your renderer decides how to place it
|
||||
}
|
||||
],
|
||||
|
||||
// ============= OUTPUT TEMPLATES (copy) =============
|
||||
getOutputs() {
|
||||
return translation.get(locale)
|
||||
},
|
||||
|
||||
// ============= NEXT STEPS =============
|
||||
getNextSteps(renderPath: string | null, candidate: GetCandidateResult) {
|
||||
const allNextSteps = translation.get(locale)
|
||||
|
||||
// Get default next steps (comes from nextsteps.json -> merged into locale data)
|
||||
const defaultSteps = allNextSteps?.default
|
||||
|
||||
if (!defaultSteps) {
|
||||
console.error('[candidate-text-engine] No default next steps found in locale.default')
|
||||
return null
|
||||
}
|
||||
|
||||
// Helper function to deep merge step objects
|
||||
const deepMergeSteps = (base: any, override: any) => {
|
||||
const result = { ...base }
|
||||
for (const [stepKey, stepOverride] of Object.entries(override)) {
|
||||
if (result[stepKey] && typeof stepOverride === 'object') {
|
||||
// Merge properties within the step
|
||||
result[stepKey] = { ...result[stepKey], ...stepOverride }
|
||||
} else {
|
||||
result[stepKey] = stepOverride
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Start with default
|
||||
let steps = { ...defaultSteps }
|
||||
|
||||
// Check if there's a role-level override (e.g., "assistant.default", "nurse.default")
|
||||
if (renderPath) {
|
||||
const roleName = renderPath.split('.')[0] // Extract "nurse", "assistant", "helper"
|
||||
const roleDefault = allNextSteps?.[`${roleName}.default`]
|
||||
if (roleDefault) {
|
||||
// Deep merge role-level defaults on top of global defaults
|
||||
steps = deepMergeSteps(steps, roleDefault)
|
||||
}
|
||||
|
||||
// Check if there's a pathway-specific override (e.g., "assistant.missinglicense")
|
||||
const pathwaySteps = allNextSteps?.[renderPath.toLowerCase()]
|
||||
if (pathwaySteps) {
|
||||
// Deep merge pathway-specific overrides on top
|
||||
steps = deepMergeSteps(steps, pathwaySteps)
|
||||
}
|
||||
}
|
||||
|
||||
// Conditionally include german step based on language requirements
|
||||
const LEVEL_RANK: Record<DB.LanguageLevel, number> = {
|
||||
A_ONE: 1, A_TWO: 2, B_ONE: 3, B_TWO: 4, C_ONE: 5, C_TWO: 6, NONE: 0
|
||||
}
|
||||
const LEVEL_DISPLAY: Record<DB.LanguageLevel, string> = {
|
||||
A_ONE: 'A1', A_TWO: 'A2', B_ONE: 'B1', B_TWO: 'B2', C_ONE: 'C1', C_TWO: 'C2', NONE: 'beginner'
|
||||
}
|
||||
const REQUIRED_GERMAN_BY_ROLE: Record<DB.NurseRecognitionGermany, DB.LanguageLevel> = {
|
||||
[NurseRecognitionGermanyValues.NURSE]: LanguageLevels.B_TWO,
|
||||
[NurseRecognitionGermanyValues.ASSISTANT_NURSE]: LanguageLevels.B_ONE,
|
||||
[NurseRecognitionGermanyValues.NURSING_HELPER]: LanguageLevels.B_ONE,
|
||||
}
|
||||
|
||||
const reqLevel = REQUIRED_GERMAN_BY_ROLE[candidate.qualificationStatusGermany!]
|
||||
const currentLevel = candidate.languageLevel || LanguageLevels.NONE
|
||||
const hasMetLanguageRequirement = LEVEL_RANK[currentLevel] >= LEVEL_RANK[reqLevel]
|
||||
|
||||
// Remove german step if language requirement is already met
|
||||
if (hasMetLanguageRequirement) {
|
||||
const { german, ...stepsWithoutGerman } = steps
|
||||
return stepsWithoutGerman
|
||||
}
|
||||
|
||||
// Interpolate language levels in german step subtitle
|
||||
if (steps.german) {
|
||||
steps = {
|
||||
...steps,
|
||||
german: {
|
||||
...steps.german,
|
||||
subtitle: steps.german.subtitle
|
||||
.replace('{currentLevel}', LEVEL_DISPLAY[currentLevel])
|
||||
.replace('{requiredLevel}', LEVEL_DISPLAY[reqLevel])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return steps
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user