NextReact / src /lib /questions.ts
Klnimri's picture
Upload 25 files
0122542 verified
import { SectionKey, SECTIONS as SECTION_DEFS } from "@/lib/sections";
export type QuestionType = "likert" | "mcq" | "text";
export type QuestionOption = { id: string; label: string };
export type BaseQuestion = {
baseId: string; // stable identifier (unique within the whole exam)
section: SectionKey;
type: QuestionType;
domain: string;
variants: [string, string, string]; // 3 rephrases (same meaning)
options?: QuestionOption[]; // for mcq
mcqScores?: Record<string, number>; // option.id -> 0..5
reverse?: boolean; // for likert (1..5 reversed)
minChars?: number; // for text
};
export type ExamQuestion = {
baseId: string;
section: SectionKey;
type: QuestionType;
domain: string;
prompt: string;
options?: QuestionOption[];
mcqScores?: Record<string, number>;
reverse?: boolean;
minChars?: number;
};
// Re-export section definitions for components that import from questions.ts
export const SECTIONS = SECTION_DEFS;
const LIKERT_LABELS = [
"Strongly disagree",
"Disagree",
"Neutral",
"Agree",
"Strongly agree"
] as const;
/** Helpers */
function likertVariants(a: string, b: string, c: string, reverse = false): [string, string, string] {
return [a, b, c];
}
function makeLikert(id: string, section: SectionKey, domain: string, v: [string, string, string], reverse = false): BaseQuestion {
return { baseId: id, section, type: "likert", domain, variants: v, reverse };
}
function makeText(id: string, section: SectionKey, domain: string, v: [string, string, string], minChars = 60): BaseQuestion {
return { baseId: id, section, type: "text", domain, variants: v, minChars };
}
function makeMCQ(
id: string,
section: SectionKey,
domain: string,
v: [string, string, string],
options: QuestionOption[],
scores: Record<string, number>
): BaseQuestion {
return { baseId: id, section, type: "mcq", domain, variants: v, options, mcqScores: scores };
}
/**
* FULL 135-base question bank (3 rephrases each).
* Notes:
* - Prompts contain no trailing generated numbers.
* - Variants are rephrases to reduce memorization/cheating.
* - One "Attention check" is included in A.
* - One text scenario is included in B (B-TEXT).
*/
export const QUESTION_BANK: BaseQuestion[] = (() => {
const q: BaseQuestion[] = [];
// -------------------------
// A (27) Psychometric & Personality
// -------------------------
const A: Array<{ domain: string; reverse?: boolean; v: [string, string, string] }> = [
{ domain: "EmotionalStability", v: likertVariants(
"In tense situations, I stay emotionally steady.",
"When situations get tense, I maintain emotional control.",
"I remain emotionally steady when tension increases."
)},
{ domain: "Conscientiousness", v: likertVariants(
"I follow through on commitments even when no one is checking.",
"Even without supervision, I complete what I promise.",
"I reliably finish tasks I commit to, even unobserved."
)},
{ domain: "StressTolerance", v: likertVariants(
"I handle time pressure without making careless mistakes.",
"Even when rushed, I keep my work accurate.",
"Under time pressure, I remain careful and accurate."
)},
{ domain: "ImpulseControl", reverse: true, v: likertVariants(
"When pressured, I can react impulsively.",
"Under stress, I may respond before fully thinking.",
"I sometimes act first and think later when stressed."
)},
{ domain: "TeamOrientation", v: likertVariants(
"I actively support teammates when workload increases.",
"When others are overloaded, I help keep operations running.",
"I step in to assist colleagues to ensure continuity."
)},
{ domain: "Adaptability", v: likertVariants(
"I adjust quickly when priorities or plans change.",
"When schedules change, I adapt without losing quality.",
"I shift my approach quickly when circumstances change."
)},
{ domain: "Integrity", reverse: true, v: likertVariants(
"Small shortcuts are acceptable if they save time.",
"Bending minor rules is fine if it speeds things up.",
"If it helps finish faster, small rule-bending is acceptable."
)},
{ domain: "Reliability", v: likertVariants(
"I plan my work so deadlines are met consistently.",
"I organize tasks to finish on time.",
"I schedule my work to meet deadlines reliably."
)},
{ domain: "DetailCare", v: likertVariants(
"I double-check important details before finalizing work.",
"I verify key details before completing a task.",
"I re-check critical details to prevent mistakes."
)},
{ domain: "FeedbackUse", v: likertVariants(
"I handle feedback without taking it personally.",
"I accept feedback calmly and use it to improve.",
"I receive feedback professionally and apply it."
)},
{ domain: "RespectUnderTension", v: likertVariants(
"I remain respectful even when others are frustrated.",
"When others are upset, I keep a respectful tone.",
"Even under tension, I stay respectful."
)},
{ domain: "FocusSwitching", v: likertVariants(
"I can switch between tasks without losing focus.",
"I transition between tasks without losing concentration.",
"I change tasks smoothly while staying focused."
)},
{ domain: "Ownership", v: likertVariants(
"I take ownership when something goes wrong.",
"If an error happens, I take responsibility.",
"I own mistakes and focus on correcting them."
)},
{ domain: "SolutionOrientation", v: likertVariants(
"I stay focused on solutions instead of blame.",
"I prioritize fixing issues rather than blaming others.",
"I focus on resolving problems, not assigning blame."
)},
{ domain: "DistractionControl", v: likertVariants(
"I avoid distractions when completing important work.",
"I keep distractions low while doing important tasks.",
"I stay away from distractions during critical work."
)},
{ domain: "Patience", v: likertVariants(
"I stay patient when procedures take time.",
"When processes take longer than expected, I remain patient.",
"I keep my patience when steps take time to complete."
)},
{ domain: "Consistency", v: likertVariants(
"I behave consistently across different teams.",
"I keep a consistent work style across groups.",
"My work behavior stays consistent across teams."
)},
{ domain: "CalmChange", v: likertVariants(
"I keep calm when plans change at the last minute.",
"Last-minute changes do not shake my calmness.",
"I remain calm during sudden last-minute changes."
)},
{ domain: "RoutineEndurance", v: likertVariants(
"I keep a positive attitude even when tasks are repetitive.",
"Even with repetitive tasks, I maintain a positive attitude.",
"Routine work does not reduce my positive attitude."
)},
{ domain: "DependabilityBoring", v: likertVariants(
"I remain reliable even when work becomes boring.",
"When work feels boring, I still stay dependable.",
"Even if a task is boring, I remain dependable."
)},
// Attention check
{ domain: "AttentionCheck", v: likertVariants(
"Attention check: please select 'Agree' for this statement.",
"To confirm attention, choose 'Agree' here.",
"For quality control, select 'Agree' for this item."
)},
// Fill remaining to 27
{ domain: "ProfessionalConflict", v: likertVariants(
"I remain professional when dealing with conflict.",
"In conflict situations, I stay professional.",
"I handle conflict professionally and calmly."
)},
{ domain: "TimeManagement", v: likertVariants(
"I manage my time effectively during busy shifts.",
"During busy shifts, I manage time well.",
"I use my time efficiently when workload is high."
)},
{ domain: "QualityAtSpeed", v: likertVariants(
"I maintain quality even when working quickly.",
"When I work fast, I still keep quality high.",
"Even at speed, I protect the quality of my work."
)},
{ domain: "ClearRoleUpdates", v: likertVariants(
"I communicate clearly when responsibilities change.",
"When roles shift, I communicate clearly.",
"I explain updates clearly when responsibilities change."
)},
{ domain: "SteadyPace", v: likertVariants(
"I keep a steady pace even during long busy periods.",
"During long busy periods, I work steadily.",
"I maintain consistent effort during extended busy times."
)}
];
A.forEach((it, i) => q.push(makeLikert(`A-${String(i + 1).padStart(2, "0")}`, "A", it.domain, it.v, Boolean(it.reverse))));
// -------------------------
// B (40) Job-Focused Capability
// -------------------------
// 10 MCQs safety/procedure, 29 Likert capability, 1 Text scenario
const mcqOptions: QuestionOption[] = [
{ id: "stop_procedure", label: "Stop and follow the documented procedure immediately." },
{ id: "continue_later", label: "Continue to avoid delay and fix it later." },
{ id: "copy_colleague", label: "Ask a colleague what they usually do and copy that." },
{ id: "report_end", label: "Proceed and report after the shift ends." }
];
const mcqScores = { stop_procedure: 5, copy_colleague: 2, report_end: 1, continue_later: 0 };
const mcqPrompts: [string, string, string] = [
"A required checklist step seems to be missing. What should you do first?",
"You notice the procedure is not being followed fully. What is your first action?",
"A compliance step appears skipped. What do you do first?"
];
for (let i = 1; i <= 10; i++) {
q.push(makeMCQ(`B-MCQ-${String(i).padStart(2, "0")}`, "B", "SafetyJudgment", mcqPrompts, mcqOptions, mcqScores));
}
const B_domains = [
{ d: "RuleAdherence", v: likertVariants(
"I follow procedures even when it slows the work down.",
"Even if it takes longer, I stick to the correct procedure.",
"I do tasks the right way even when it costs time."
)},
{ d: "DecisionPressure", v: likertVariants(
"Under pressure, I decide using rules and evidence.",
"When urgency is high, I prioritize safety and accuracy in decisions.",
"Even in urgency, I base decisions on facts and procedure."
)},
{ d: "AttentionToDetail", v: likertVariants(
"I notice small issues early and correct them.",
"I detect small errors before they become bigger problems.",
"I catch details that could cause later errors."
)},
{ d: "SafetyMindset", v: likertVariants(
"If I notice a risk, I stop and reassess before continuing.",
"I pause work if I believe safety could be compromised.",
"When I see a hazard, I address it before proceeding."
)},
{ d: "ClearCommunication", v: likertVariants(
"When priorities shift, I clarify roles and expectations.",
"I communicate clearly when tasks change suddenly.",
"I keep communication clear during rapid changes."
)},
{ d: "ProcedureDiscipline", v: likertVariants(
"I keep accurate records when documentation is required.",
"When documentation is needed, I complete it correctly.",
"I ensure required documentation is accurate and complete."
)},
{ d: "SituationalAwareness", v: likertVariants(
"I notice changes around me that could affect safety.",
"I stay aware of surroundings that could create risks.",
"I remain alert to conditions that might affect safety."
)},
{ d: "PrioritySetting", v: likertVariants(
"I prioritize tasks based on safety and urgency.",
"When multiple tasks compete, I prioritize correctly.",
"I choose priorities using safety, urgency, and procedure."
)},
{ d: "ShortcutRisk", rev: true, v: likertVariants(
"If I’m confident, I may skip steps to move faster.",
"I’m willing to bypass steps when the outcome seems obvious.",
"Taking shortcuts is worth it if it speeds things up."
)},
{ d: "Composure", v: likertVariants(
"When a situation escalates, I remain calm and professional.",
"I keep composure when problems happen unexpectedly.",
"I stay calm when operations become chaotic."
)}
];
// Create 29 items by cycling and lightly varying domains
for (let i = 1; i <= 29; i++) {
const t = B_domains[(i - 1) % B_domains.length];
q.push(makeLikert(`B-${String(i).padStart(2, "0")}`, "B", t.d, t.v, Boolean((t as any).rev)));
}
q.push(makeText(
"B-TEXT",
"B",
"OperationalScenario",
[
"Scenario: A flight is delayed, passengers are frustrated, and the team is short-staffed. Describe your actions to keep operations safe and compliant.",
"Scenario: During disruption and heavy workload, explain how you would prioritize tasks and communicate while maintaining safety and procedure.",
"Scenario: Under operational pressure, explain step-by-step how you keep safety, rules, and service quality."
],
90
));
// -------------------------
// C (20) Work Stressors & Burnout Risk
// -------------------------
for (let i = 1; i <= 20; i++) {
const rev = i % 5 === 0;
q.push(makeLikert(
`C-${String(i).padStart(2, "0")}`,
"C",
rev ? "RecoveryCapacity" : "WorkloadTolerance",
[
rev ? "After demanding workdays, I struggle to recover before the next shift." : "I can handle sustained workload without feeling overwhelmed.",
rev ? "I often start shifts without feeling fully recovered." : "I remain effective when workload stays high for several days.",
rev ? "I frequently begin work still tired from prior days." : "I manage time pressure without building excessive stress."
],
rev
));
}
// -------------------------
// D (20) Workload Sustainability
// -------------------------
for (let i = 1; i <= 20; i++) {
const rev = i % 4 === 0;
q.push(makeLikert(
`D-${String(i).padStart(2, "0")}`,
"D",
rev ? "ErrorRiskUnderLoad" : "SustainedPerformance",
[
rev ? "When workload stays high, my accuracy drops noticeably." : "I maintain consistent performance across long busy periods.",
rev ? "During extended busy periods, I make more avoidable mistakes." : "Even with heavy demand, I keep my output stable.",
rev ? "Under prolonged pressure, my attention slips." : "I manage energy so I can perform reliably long-term."
],
rev
));
}
// -------------------------
// E (13) Psychological Safety & Team Fit
// -------------------------
for (let i = 1; i <= 13; i++) {
const rev = i % 6 === 0;
q.push(makeLikert(
`E-${String(i).padStart(2, "0")}`,
"E",
rev ? "SpeakUpBarrier" : "SpeakUpCulture",
[
rev ? "If a supervisor is wrong, it is better to stay quiet to avoid conflict." : "I would speak up respectfully if I notice a safety or compliance risk.",
rev ? "I avoid raising concerns when senior staff are involved." : "I raise issues early when I believe safety could be affected.",
rev ? "It is not my place to question decisions, even if risks exist." : "I am comfortable reporting risks through the proper channels."
],
rev
));
}
// -------------------------
// F (15) Wellbeing & Functional Readiness
// -------------------------
for (let i = 1; i <= 15; i++) {
const rev = i % 5 === 0;
q.push(makeLikert(
`F-${String(i).padStart(2, "0")}`,
"F",
rev ? "HelpSeekingBarrier" : "FunctionalReadiness",
[
rev ? "If I were struggling, I would avoid seeking support at work." : "I manage stress in a way that keeps me functional and reliable at work.",
rev ? "I would hide difficulties rather than ask for help or guidance." : "I recognize early signs of fatigue and take appropriate steps to stay safe.",
rev ? "I prefer to handle serious workload strain alone without involving anyone." : "I remain ready and effective across different shifts and demands."
],
rev
));
}
return q;
})();
type PRNG = { rand: () => number; pick3: () => 0 | 1 | 2 };
function seeded(seed: string): PRNG {
let h = 2166136261;
for (let i = 0; i < seed.length; i++) {
h ^= seed.charCodeAt(i);
h = Math.imul(h, 16777619);
}
function rand() {
h += 0x6d2b79f5;
let t = Math.imul(h ^ (h >>> 15), 1 | h);
t ^= t + Math.imul(t ^ (t >>> 7), 61 | t);
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
}
return { rand, pick3: () => Math.floor(rand() * 3) as 0 | 1 | 2 };
}
/**
* Build exam by selecting ONE variant per base question.
* FIX: prevents prompt duplication within the same section by trying other variants;
* last resort uses a zero-width marker so the internal string is unique (candidate sees same text).
*/
export function buildExam(seed: string) {
const { rand, pick3 } = seeded(seed);
const bySection: Record<SectionKey, BaseQuestion[]> = { A: [], B: [], C: [], D: [], E: [], F: [] };
for (const b of QUESTION_BANK) bySection[b.section].push(b);
const examBySection: Record<SectionKey, ExamQuestion[]> = { A: [], B: [], C: [], D: [], E: [], F: [] };
const flat: ExamQuestion[] = [];
for (const s of ["A", "B", "C", "D", "E", "F"] as SectionKey[]) {
const used = new Set<string>();
const pool = bySection[s].slice();
// deterministic shuffle per section
for (let i = pool.length - 1; i > 0; i--) {
const j = Math.floor(rand() * (i + 1));
[pool[i], pool[j]] = [pool[j], pool[i]];
}
for (const b of pool) {
let vi = pick3();
let prompt = b.variants[vi];
if (used.has(prompt)) {
const alts: (0 | 1 | 2)[] = [0, 1, 2].filter((x) => x !== vi) as any;
for (const a of alts) {
const p2 = b.variants[a];
if (!used.has(p2)) {
vi = a;
prompt = p2;
break;
}
}
}
// last resort: make string unique internally without changing visible content
if (used.has(prompt)) {
prompt = prompt + "\u200B";
}
used.add(prompt);
const eq: ExamQuestion = {
baseId: b.baseId,
section: b.section,
type: b.type,
domain: b.domain,
prompt,
options: b.options ?? (b.type === "likert" ? LIKERT_LABELS.map((l) => ({ id: l, label: l })) : undefined),
mcqScores: b.mcqScores,
reverse: b.reverse,
minChars: b.minChars
};
examBySection[s].push(eq);
flat.push(eq);
}
}
return { examBySection, flat };
}