Everything another Base44 app needs to reproduce the video module and knowledge-check testing system — entities, the YouTube player with sequential unlocking, the quiz modal with pass/fail and retake logic, the question bank, and the certificate trigger. Download the guide and hand it to the other app's builder.
Every question with the correct answer spelled out — readable as a document, or download as Markdown/HTML. Use this to create QuizQuestion database records in the other app, or as a reference for instructors.
# Quiz Questions & Answers — Full Guide (All 6 Modules)
This guide contains the **actual** knowledge-check questions used in the Build & Program A Pool training system — pulled from the live QuizQuestion database. Both the **primary** set (first attempt) and the **retake** set (used when a participant scores below 70%) are included, with the correct answer marked for each.
**Totals:** 40 questions across 6 modules (primary + retake sets).
---
## How to use this in another Base44 app
1. **Option A — Hardcoded fallback:** Copy the JS export from the Testing Guide page (`quizQuestions.js`) into `src/components/knowledge/quizQuestions.js` in the other app. The VideoSection component falls back to these when no database questions exist.
2. **Option B — Database records (recommended):** Create `QuizQuestion` entity records using the data below. Each record needs:
- `video_id` (integer 1–6)
- `question` (string)
- `options` (array of strings)
- `correct_index` (0-based integer — the position of the correct answer in the options array)
- `question_set` ("primary" or "retake")
- `order` (integer, display order within the module)
---
## Module 1 — Project Planning & Bodies of Water
### Primary Set
**Q1.** What is the minimum number of pools a project should consider?
- A. 1
- B. 2 ✅
- C. 3
- D. 4 or more
**Correct: B (2)**
**Q2.** Every "space" in a facility should have at least how many income producing opportunities?
- A. 1
- B. 2
- C. 3 ✅
- D. 4
**Correct: C (3)**
**Q3.** What does T.A.D. stand for?
- A. Total Aquatic Design
- B. Temporary Apportioned Deck
- C. Tea Apples and Ducks
- D. Temperature Access and Depth ✅
**Correct: D (Temperature Access and Depth)**
### Retake Set
**Q1.** What is the recommended minimum number of distinct bodies of water to include in an aquatic project plan?
- A. 1
- B. 2 ✅
- C. 3
- D. 4 or more
**Correct: B (2)**
**Q2.** How many revenue-generating potentials should ideally be incorporated into every single zone of an aquatic facility?
- A. 1
- B. 2
- C. 3 ✅
- D. 4
**Correct: C (3)**
**Q3.** Which acronym defines the parameters of Temperature, Access, and Depth?
- A. T.A.P.
- B. A.D.T.
- C. D.E.P.
- D. T.A.D. ✅
**Correct: D (T.A.D.)**
---
## Module 2 — Programming & The Prime Directive
### Primary Set
**Q1.** What is the 3-word Prime Directive?
- A. Programming Proceeds Design ✅
- B. The Final Frontier
- C. Design Develop and Deliver
- D. Temperature Access and Depth
**Correct: A (Programming Proceeds Design)**
**Q2.** What is the most versatile water temperature for vertical exercise?
- A. 72-74°
- B. 75-80°
- C. 81-86°
- D. 87-89° ✅
**Correct: D (87-89°)**
**Q3.** What percentage of adults in the USA do not know how to swim?
- A. 10% ✅
- B. 30%
- C. 50%
- D. 75%
**Correct: A (10%)**
### Retake Set
**Q1.** What is the foundational principle known as the 3-word Prime Directive in aquatic facility planning?
- A. Programming Proceeds Design ✅
- B. Plan Provide Profit
- C. People Purpose Pool
- D. Project Process Delivery
**Correct: A (Programming Proceeds Design)**
**Q2.** In the context of water-based fitness, what is considered the most versatile temperature range?
- A. 70-74°
- B. 78-82°
- C. 83-86°
- D. 87-89° ✅
**Correct: D (87-89°)**
**Q3.** Approximately what percentage of the US adult population lacks basic swimming proficiency?
- A. 10% ✅
- B. 25%
- C. 40%
- D. 60%
**Correct: A (10%)**
---
## Module 3 — Financial Planning & Cost Estimating
### Primary Set
**Q1.** What are the 2 parts "everything" can be divided into?
- A. Good vs. Evil
- B. Income / Expense ✅
- C. Black and White
- D. Debt / Credit
**Correct: B (Income / Expense)**
**Q2.** TRUE or FALSE: With a cost "guesstimate" you can usually use $450 to $550 per square foot for the actual pools?
- A. True ✅
- B. False
**Correct: A (True)**
**Q3.** A 6 lane 25-yard main pool can handle up to how many people in a community?
- A. 10,000
- B. 15,000
- C. 20,000
- D. 25,000 ✅
**Correct: D (25,000)**
### Retake Set
**Q1.** Financial planning for an aquatic facility is categorized into what two primary components?
- A. Assets and Liabilities
- B. Income and Expense ✅
- C. Construction and Maintenance
- D. Grants and Loans
**Correct: B (Income and Expense)**
**Q2.** True or False: A standard rough estimate for the cost per square foot of the pool basin structure is between $450 and $550?
- A. True ✅
- B. False
**Correct: A (True)**
**Q3.** What population size can typically be supported by a single 6-lane 25-yard main pool?
- A. 5,000
- B. 10,000
- C. 18,000
- D. 25,000 ✅
**Correct: D (25,000)**
---
## Module 4 — Construction, Covers & Green Technology
### Primary Set
**Q1.** What Virginia USA Swim Team renovated a Macy's© department store in a mall to create a 50-meter competition facility?
- A. Turtles Swimming
- B. NOVA of Virginia ✅
- C. Virginia Aquatics
- D. FMC Aquatics
**Correct: B (NOVA of Virginia)**
**Q2.** TRUE or FALSE: Indoor pools should be covered when not in use?
- A. True ✅
- B. False
**Correct: A (True)**
**Q3.** True or False: Passive Solar is currently the most popular Green Technology used for pools?
- A. True ✅
- B. False
**Correct: A (True)**
**Q4.** Why remove toys from the pool when not in use?
- A. They get damaged by sun
- B. They can attract children to the water ✅
- C. They clog drains
- D. They fade colors
**Correct: B (They can attract children to the water)**
### Retake Set
**Q1.** Which notable Virginia-based organization repurposed a former retail department store space into a competitive aquatic center?
- A. USA Swimming National
- B. NOVA of Virginia ✅
- C. Virginia Aquatic Center
- D. Mall Aquatics Group
**Correct: B (NOVA of Virginia)**
**Q2.** True or False: It is best practice to utilize covers on indoor pool surfaces when the facility is not currently operating?
- A. True ✅
- B. False
**Correct: A (True)**
**Q3.** True or False: Is Passive Solar recognized as the predominant green technology utilized in modern aquatic facility design?
- A. True ✅
- B. False
**Correct: A (True)**
**Q4.** For safety reasons, what is the primary purpose of removing pool toys and floats after use?
- A. To extend the life of the plastic
- B. To avoid creating an attractive nuisance for unattended children ✅
- C. To prevent them from floating into the filtration system
- D. To comply with health department color codes
**Correct: B (To avoid creating an attractive nuisance for unattended children)**
---
## Module 5 — Filtration, Disinfection & Water Safety
### Primary Set
**Q1.** True or False: Teaching pools should use skimmers, while Competition pools should use gutters?
- A. True
- B. False ✅
**Correct: B (False)**
**Q2.** What is necessary for water disinfection?
- A. Chlorine ✅
- B. Salt
- C. Ozone
- D. UV
**Correct: A (Chlorine)**
**Q3.** TRUE or FALSE: The most effective filter for commercial aquatics is a SAND FILTER.
- A. True
- B. False ✅
**Correct: B (False)**
**Q4.** Which is a sign of a secondary or delayed drowning concern?
- A. No symptoms ever
- B. Persistent coughing, trouble breathing, or chest pain after an incident ✅
- C. Normal appetite
- D. Slightly cold skin
**Correct: B (Persistent coughing, trouble breathing, or chest pain after an incident)**
### Retake Set
**Q1.** True or False: The general rule for commercial pools is that teaching pools must use skimmers while competition pools must exclusively use gutters?
- A. True
- B. False ✅
**Correct: B (False)**
**Q2.** Which of these is explicitly identified as essential for the standard chemical disinfection of swimming pool water?
- A. Chlorine ✅
- B. Salt
- C. Ozone
- D. UV treatment
**Correct: A (Chlorine)**
**Q3.** True or False: The industry standard for the most effective filtration system for all commercial aquatic facilities is sand filtration?
- A. True
- B. False ✅
**Correct: B (False)**
**Q4.** If a patron experiences an aquatic incident, what indicates a need for monitoring regarding delayed drowning?
- A. A sudden increase in thirst
- B. Difficulty breathing, persistent cough, or chest discomfort ✅
- C. The person feels overly warm shortly after exiting the pool
- D. A lack of appetite immediately following the event
**Correct: B (Difficulty breathing, persistent cough, or chest discomfort)**
---
## Module 6 — Construction Alternatives & Feasibility
### Primary Set
**Q1.** What is a viable alternative to concrete pools?
- A. Vinyl Liner
- B. Steel ✅
- C. Alumiunum
- D. Tile
**Correct: B (Steel)**
**Q2.** TRUE or FALSE: There are modern structure options that allow for seasonal outdoor and indoor options that are aluminum frame buildings that can quickly go up and down every 180 days.
- A. True ✅
- B. False
**Correct: A (True)**
**Q3.** What is a lite feasibility study provided by Total Aquatic Programming called?
- A. Feasibility Lite
- B. Enterprise Plan ✅
- C. Kobayashi Maru
- D. Saving Pools, Saves Lives
**Correct: B (Enterprise Plan)**
### Retake Set
**Q1.** Aside from concrete, which material serves as a practical, alternative construction option for pool shells?
- A. High-density plastic
- B. Steel ✅
- C. Composite wood
- D. Fiberglass mesh
**Correct: B (Steel)**
**Q2.** True or False: There are modern modular building solutions featuring aluminum frames that allow an aquatic space to convert between indoor and outdoor use on a seasonal basis?
- A. True ✅
- B. False
**Correct: A (True)**
**Q3.** What is the specific name of the condensed, streamlined feasibility assessment offered by Total Aquatic Programming?
- A. Mini-Study
- B. Enterprise Plan ✅
- C. Aquatic Audit
- D. Pool Viability Report
**Correct: B (Enterprise Plan)**
---
## Summary
| Module | Topic | Primary Qs | Retake Qs |
|--------|-------|-----------|-----------|
| 1 | Project Planning & Bodies of Water | 3 | 3 |
| 2 | Programming & The Prime Directive | 3 | 3 |
| 3 | Financial Planning & Cost Estimating | 3 | 3 |
| 4 | Construction, Covers & Green Technology | 4 | 4 |
| 5 | Filtration, Disinfection & Water Safety | 4 | 4 |
| 6 | Construction Alternatives & Feasibility | 3 | 3 |
| **Total** | | **20** | **20** |
**Pass threshold:** 70%.
**Retake policy:** Unlimited retakes; retake uses the alternate question set.
The same questions as a JS module (\`quizQuestions.js\`) — drop it into the other app at \`src/components/knowledge/quizQuestions.js\` and the VideoSection component will use it as a hardcoded fallback.
// Knowledge-check questions for each module video.
// Pulled from the live QuizQuestion database (Build & Program A Pool workshop).
// 3-4 multiple-choice questions per module, primary + retake sets.
export const QUIZ_QUESTIONS = {
1: [
{
question: 'What is the minimum number of pools a project should consider?',
options: ['1', '2', '3', '4 or more'],
correctIndex: 1,
},
{
question: 'Every "space" in a facility should have at least how many income producing opportunities?',
options: ['1', '2', '3', '4'],
correctIndex: 2,
},
{
question: 'What does T.A.D. stand for?',
options: ['Total Aquatic Design', 'Temporary Apportioned Deck', 'Tea Apples and Ducks', 'Temperature Access and Depth'],
correctIndex: 3,
},
],
2: [
{
question: 'What is the 3-word Prime Directive?',
options: ['Programming Proceeds Design', 'The Final Frontier', 'Design Develop and Deliver', 'Temperature Access and Depth'],
correctIndex: 0,
},
{
question: 'What is the most versatile water temperature for vertical exercise?',
options: ['72-74°', '75-80°', '81-86°', '87-89°'],
correctIndex: 3,
},
{
question: 'What percentage of adults in the USA do not know how to swim?',
options: ['10%', '30%', '50%', '75%'],
correctIndex: 0,
},
],
3: [
{
question: 'What are the 2 parts "everything" can be divided into?',
options: ['Good vs. Evil', 'Income / Expense', 'Black and White', 'Debt / Credit'],
correctIndex: 1,
},
{
question: 'TRUE or FALSE: With a cost "guesstimate" you can usually use $450 to $550 per square foot for the actual pools?',
options: ['True', 'False'],
correctIndex: 0,
},
{
question: 'A 6 lane 25-yard main pool can handle up to how many people in a community?',
options: ['10,000', '15,000', '20,000', '25,000'],
correctIndex: 3,
},
],
4: [
{
question: "What Virginia USA Swim Team renovated a Macy's© department store in a mall to create a 50-meter competition facility?",
options: ['Turtles Swimming', 'NOVA of Virginia', 'Virginia Aquatics', 'FMC Aquatics'],
correctIndex: 1,
},
{
question: 'TRUE or FALSE: Indoor pools should be covered when not in use?',
options: ['True', 'False'],
correctIndex: 0,
},
{
question: 'True or False: Passive Solar is currently the most popular Green Technology used for pools?',
options: ['True', 'False'],
correctIndex: 0,
},
{
question: 'Why remove toys from the pool when not in use?',
options: ['They get damaged by sun', 'They can attract children to the water', 'They clog drains', 'They fade colors'],
correctIndex: 1,
},
],
5: [
{
question: 'True or False: Teaching pools should use skimmers, while Competition pools should use gutters?',
options: ['True', 'False'],
correctIndex: 1,
},
{
question: 'What is necessary for water disinfection?',
options: ['Chlorine', 'Salt', 'Ozone', 'UV'],
correctIndex: 0,
},
{
question: 'TRUE or FALSE: The most effective filter for commercial aquatics is a SAND FILTER.',
options: ['True', 'False'],
correctIndex: 1,
},
{
question: 'Which is a sign of a secondary or delayed drowning concern?',
options: ['No symptoms ever', 'Persistent coughing, trouble breathing, or chest pain after an incident', 'Normal appetite', 'Slightly cold skin'],
correctIndex: 1,
},
],
6: [
{
question: 'What is a viable alternative to concrete pools?',
options: ['Vinyl Liner', 'Steel', 'Alumiunum', 'Tile'],
correctIndex: 1,
},
{
question: 'TRUE or FALSE: There are modern structure options that allow for seasonal outdoor and indoor options that are aluminum frame buildings that can quickly go up and down every 180 days.',
options: ['True', 'False'],
correctIndex: 0,
},
{
question: 'What is a lite feasibility study provided by Total Aquatic Programming called?',
options: ['Feasibility Lite', 'Enterprise Plan', 'Kobayashi Maru', 'Saving Pools, Saves Lives'],
correctIndex: 1,
},
],
};
// Alternate question set used when a participant retakes a quiz after scoring below 70%.
export const QUIZ_QUESTIONS_RETAKE = {
1: [
{
question: 'What is the recommended minimum number of distinct bodies of water to include in an aquatic project plan?',
options: ['1', '2', '3', '4 or more'],
correctIndex: 1,
},
{
question: 'How many revenue-generating potentials should ideally be incorporated into every single zone of an aquatic facility?',
options: ['1', '2', '3', '4'],
correctIndex: 2,
},
{
question: 'Which acronym defines the parameters of Temperature, Access, and Depth?',
options: ['T.A.P.', 'A.D.T.', 'D.E.P.', 'T.A.D.'],
correctIndex: 3,
},
],
2: [
{
question: 'What is the foundational principle known as the 3-word Prime Directive in aquatic facility planning?',
options: ['Programming Proceeds Design', 'Plan Provide Profit', 'People Purpose Pool', 'Project Process Delivery'],
correctIndex: 0,
},
{
question: 'In the context of water-based fitness, what is considered the most versatile temperature range?',
options: ['70-74°', '78-82°', '83-86°', '87-89°'],
correctIndex: 3,
},
{
question: 'Approximately what percentage of the US adult population lacks basic swimming proficiency?',
options: ['10%', '25%', '40%', '60%'],
correctIndex: 0,
},
],
3: [
{
question: 'Financial planning for an aquatic facility is categorized into what two primary components?',
options: ['Assets and Liabilities', 'Income and Expense', 'Construction and Maintenance', 'Grants and Loans'],
correctIndex: 1,
},
{
question: 'True or False: A standard rough estimate for the cost per square foot of the pool basin structure is between $450 and $550?',
options: ['True', 'False'],
correctIndex: 0,
},
{
question: 'What population size can typically be supported by a single 6-lane 25-yard main pool?',
options: ['5,000', '10,000', '18,000', '25,000'],
correctIndex: 3,
},
],
4: [
{
question: 'Which notable Virginia-based organization repurposed a former retail department store space into a competitive aquatic center?',
options: ['USA Swimming National', 'NOVA of Virginia', 'Virginia Aquatic Center', 'Mall Aquatics Group'],
correctIndex: 1,
},
{
question: 'True or False: It is best practice to utilize covers on indoor pool surfaces when the facility is not currently operating?',
options: ['True', 'False'],
correctIndex: 0,
},
{
question: 'True or False: Is Passive Solar recognized as the predominant green technology utilized in modern aquatic facility design?',
options: ['True', 'False'],
correctIndex: 0,
},
{
question: 'For safety reasons, what is the primary purpose of removing pool toys and floats after use?',
options: ['To extend the life of the plastic', 'To avoid creating an attractive nuisance for unattended children', 'To prevent them from floating into the filtration system', 'To comply with health department color codes'],
correctIndex: 1,
},
],
5: [
{
question: 'True or False: The general rule for commercial pools is that teaching pools must use skimmers while competition pools must exclusively use gutters?',
options: ['True', 'False'],
correctIndex: 1,
},
{
question: 'Which of these is explicitly identified as essential for the standard chemical disinfection of swimming pool water?',
options: ['Chlorine', 'Salt', 'Ozone', 'UV treatment'],
correctIndex: 0,
},
{
question: 'True or False: The industry standard for the most effective filtration system for all commercial aquatic facilities is sand filtration?',
options: ['True', 'False'],
correctIndex: 1,
},
{
question: 'If a patron experiences an aquatic incident, what indicates a need for monitoring regarding delayed drowning?',
options: ['A sudden increase in thirst', 'Difficulty breathing, persistent cough, or chest discomfort', 'The person feels overly warm shortly after exiting the pool', 'A lack of appetite immediately following the event'],
correctIndex: 1,
},
],
6: [
{
question: 'Aside from concrete, which material serves as a practical, alternative construction option for pool shells?',
options: ['High-density plastic', 'Steel', 'Composite wood', 'Fiberglass mesh'],
correctIndex: 1,
},
{
question: 'True or False: There are modern modular building solutions featuring aluminum frames that allow an aquatic space to convert between indoor and outdoor use on a seasonal basis?',
options: ['True', 'False'],
correctIndex: 0,
},
{
question: 'What is the specific name of the condensed, streamlined feasibility assessment offered by Total Aquatic Programming?',
options: ['Mini-Study', 'Enterprise Plan', 'Aquatic Audit', 'Pool Viability Report'],
correctIndex: 1,
},
],
};
# Video Module & Knowledge-Check Testing System — Replication Guide
This document explains exactly how the training video and quiz testing system works in this app, so another Base44 app can reproduce the same architecture end-to-end. It covers the data model, the YouTube video player with sequential module unlocking, the quiz modal with pass/fail and retake logic, the question bank structure, and how completion is tracked to trigger certificate issuance.
---
## How it works (overview)
1. An admin creates **QuizQuestion** records for each module — a "primary" set and an optional "retake" set (used when a participant fails the first attempt).
2. A participant opens the **Knowledge Base** page, which renders a **VideoSection** component showing six module cards in a grid.
3. Module 1 is unlocked; all others are locked until the previous module is completed.
4. The participant clicks an unlocked module card. A lightbox opens with a YouTube IFrame Player embedding that module's video.
5. When the video reaches the end (detected via the YouTube API `onStateChange` event), the **QuizModal** opens automatically.
6. The participant answers all multiple-choice questions and submits. The score is calculated client-side.
7. If the score is ≥ 70%, the participant clicks "Complete Module" — this creates/updates a **VideoProgress** record marking the module complete and storing the quiz score.
8. If the score is < 70%, the participant clicks "Retake Quiz" and gets an alternate question set. There is no limit on retakes.
9. Completing a module unlocks the next one. The progress counter updates in real time.
10. When all six modules are complete, a name prompt appears. The participant enters their name and the **CertificateView** renders. A workflow fires automatically and issues a permanent certificate (see the Certificate Replication Guide for that part).
---
## 1. Entities
You need two entities for the testing system (plus VideoProgress, which is shared with the certificate system).
### VideoProgress (tracks module completion — one record per module per user)
Path: `base44/entities/VideoProgress.jsonc`
```jsonc
{
"name": "VideoProgress",
"type": "object",
"properties": {
"video_id": { "type": "integer", "description": "Module video id" },
"video_title": { "type": "string", "description": "Module video title" },
"completed": { "type": "boolean", "default": false, "description": "Whether the user finished watching this video and passed the quiz" },
"completed_at": { "type": "string", "format": "date-time", "description": "When the video was marked complete" },
"quiz_score": { "type": "integer", "description": "Number of quiz questions answered correctly" },
"quiz_total": { "type": "integer", "description": "Total number of quiz questions for this module" },
"description": { "type": "string", "maxLength": 1000, "description": "Optional description" }
},
"required": ["video_id", "video_title"],
"rls": {
"read": { "$or": [ { "created_by_id": "{{user.id}}" }, { "user_condition": { "role": "admin" } } ] },
"create": { "created_by_id": "{{user.id}}" },
"update": { "created_by_id": "{{user.id}}" },
"delete": { "$or": [ { "created_by_id": "{{user.id}}" }, { "user_condition": { "role": "admin" } } ] }
}
}
```
**Key design choice:** RLS lets a user create and update only their own VideoProgress records, but admins can read all of them. The `created_by_id` field is the link between a progress record and the user who earned it — the certificate backend function uses it to resolve the user.
### QuizQuestion (the question bank — admin-managed, per module)
Path: `base44/entities/QuizQuestion.jsonc`
```jsonc
{
"name": "QuizQuestion",
"type": "object",
"properties": {
"video_id": { "type": "integer", "description": "Module video id this question belongs to" },
"question": { "type": "string", "description": "The question text" },
"options": { "type": "array", "items": { "type": "string" }, "default": [], "description": "Multiple-choice options" },
"correct_index": { "type": "integer", "description": "Index (0-based) of the correct option" },
"order": { "type": "integer", "default": 0, "description": "Display order within the module" },
"question_set": { "type": "string", "enum": ["primary", "retake"], "default": "primary", "description": "Which attempt this question belongs to (primary = first attempt, retake = second attempt)" },
"description": { "type": "string", "maxLength": 1000, "description": "Optional description" }
},
"required": ["video_id", "question", "options", "correct_index"],
"rls": {
"read": { "$or": [ { "created_by_id": "{{user.id}}" }, { "user_condition": { "role": "admin" } } ] },
"create": { "user_condition": { "role": "admin" } },
"update": { "user_condition": { "role": "admin" } },
"delete": { "user_condition": { "role": "admin" } }
}
}
```
**Key design choice:** The `question_set` field distinguishes first-attempt questions from retake questions. When a participant fails and clicks "Retake Quiz", the component fetches the `retake` set for that module. If no retake questions exist in the database, it falls back to a hardcoded alternate set (see section 4).
---
## 2. The VideoSection component (video player + module grid)
Path: `src/components/knowledge/VideoSection.jsx`
This is the core component. It renders the module grid, manages the YouTube player, handles sequential unlocking, and coordinates with the QuizModal.
### Module definition
Define your modules as a static array. Each module has an `id` (integer, used as `video_id` in entities), a `title`, and a `youtubeId`:
```js
const VIDEOS = [
{ id: 1, title: 'Module 1', youtubeId: 'ugKw-bqTwkA' },
{ id: 2, title: 'Module 2', youtubeId: 'Pbmr4rJAhE0' },
{ id: 3, title: 'Module 3', youtubeId: 'dxldSZC9OFI' },
{ id: 4, title: 'Module 4', youtubeId: 'XFxwkuRoimc' },
{ id: 5, title: 'Module 5', youtubeId: 'ILlOjVXhwY4' },
{ id: 6, title: 'Module 6', youtubeId: 'aNmuV_I9e-8' },
];
```
### Loading the YouTube IFrame API
The YouTube IFrame Player API is loaded once and cached in a module-level promise:
```js
let apiPromise = null;
function loadYouTubeAPI() {
if (apiPromise) return apiPromise;
apiPromise = new Promise((resolve) => {
if (window.YT && window.YT.Player) return resolve(window.YT);
const existing = document.querySelector('script[src="https://www.youtube.com/iframe_api"]');
if (!existing) {
const tag = document.createElement('script');
tag.src = 'https://www.youtube.com/iframe_api';
document.head.appendChild(tag);
}
const prev = window.onYouTubeIframeAPIReady;
window.onYouTubeIframeAPIReady = () => {
prev && prev();
resolve(window.YT);
};
});
return apiPromise;
}
```
### State
```js
const [progress, setProgress] = useState({}); // { [videoId]: progressRecord }
const [loading, setLoading] = useState(true);
const [activeVideo, setActiveVideo] = useState(null); // which video is in the lightbox
const [showQuiz, setShowQuiz] = useState(false); // quiz modal open?
const [isRetake, setIsRetake] = useState(false); // using retake question set?
const [quizQuestions, setQuizQuestions] = useState({}); // { [videoId]: { primary: [...], retake: [...] } }
const playerRef = useRef(null);
const playerContainerRef = useRef(null);
```
### Loading data on mount
```js
useEffect(() => {
// Load the user (for default certificate name)
base44.auth.me().then(setUser).catch(() => {});
// Load all VideoProgress records for this user, keyed by video_id
base44.entities.VideoProgress.list()
.then((records) => {
const map = {};
records.forEach((r) => { map[r.video_id] = r; });
setProgress(map);
})
.catch(() => {})
.finally(() => setLoading(false));
// Load all QuizQuestion records, grouped by video_id and question_set
base44.entities.QuizQuestion.list('order')
.then((records) => {
const map = {};
records.forEach((r) => {
const set = r.question_set || 'primary';
if (!map[r.video_id]) map[r.video_id] = {};
if (!map[r.video_id][set]) map[r.video_id][set] = [];
map[r.video_id][set].push(toQuizFormat(r));
});
setQuizQuestions(map);
})
.catch(() => {});
}, []);
```
### Sequential unlocking
A module is unlocked only if the previous module is completed:
```js
const isUnlocked = useCallback((index) => {
if (index === 0) return true; // first module always unlocked
const prevVideo = VIDEOS[index - 1];
return !!progress[prevVideo.id]?.completed;
}, [progress]);
```
### Creating the YouTube player when a module opens
```js
useEffect(() => {
if (!activeVideo) return;
let cancelled = false;
loadYouTubeAPI().then((YT) => {
if (cancelled || !playerContainerRef.current) return;
playerRef.current = new YT.Player(playerContainerRef.current, {
videoId: activeVideo.youtubeId,
playerVars: { autoplay: 1, rel: 0, modestbranding: 1 },
events: {
onStateChange: (e) => {
// 0 = ended — open the quiz when the video finishes
if (e.data === YT.PlayerState.ENDED) {
setShowQuiz(true);
}
},
},
});
});
return () => {
cancelled = true;
if (playerRef.current) {
try { playerRef.current.destroy(); } catch (e) {}
playerRef.current = null;
}
};
}, [activeVideo]);
```
**Important:** The cleanup function destroys the player when the lightbox closes or the active video changes. Without this, you get orphaned players and audio bleeding between modules.
### Marking a module complete
Called after the participant passes the quiz:
```js
const markComplete = useCallback(async (video, score, total) => {
setMarking(true);
try {
const existing = progress[video.id];
const payload = {
completed: true,
completed_at: new Date().toISOString(),
quiz_score: score,
quiz_total: total,
};
if (existing) {
const updated = await base44.entities.VideoProgress.update(existing.id, payload);
setProgress((p) => ({ ...p, [video.id]: updated }));
} else {
const created = await base44.entities.VideoProgress.create({
video_id: video.id,
video_title: video.title,
...payload,
});
setProgress((p) => ({ ...p, [video.id]: created }));
}
} catch (e) {
// ignore — completion still tracked in UI
} finally {
setMarking(false);
}
}, [progress]);
```
### Checking if all modules are complete
```js
const completedCount = VIDEOS.filter((v) => {
const rec = progress[v.id];
if (!rec?.completed) return false;
if (rec.quiz_total > 0 && rec.quiz_score != null) {
return rec.quiz_score / rec.quiz_total >= 0.7;
}
return false;
}).length;
const allComplete = completedCount === VIDEOS.length;
```
When `allComplete` is true, a name prompt appears. After the participant enters their name, the `CertificateView` component renders (see the Certificate Replication Guide).
### Passing questions to the QuizModal
The component chooses the primary or retake question set, with fallback to hardcoded defaults:
```js
questions={isRetake
? (quizQuestions[activeVideo.id]?.retake?.length
? quizQuestions[activeVideo.id].retake
: (QUIZ_QUESTIONS_RETAKE[activeVideo.id] || QUIZ_QUESTIONS[activeVideo.id] || []))
: (quizQuestions[activeVideo.id]?.primary?.length
? quizQuestions[activeVideo.id].primary
: (QUIZ_QUESTIONS[activeVideo.id] || []))}
```
The fallback to `QUIZ_QUESTIONS` / `QUIZ_QUESTIONS_RETAKE` (hardcoded in `quizQuestions.js`) means the system works even before any admin adds questions to the database. Once database questions exist, they take priority.
---
## 3. The QuizModal component (quiz UI + scoring)
Path: `src/components/knowledge/QuizModal.jsx`
Renders the quiz overlay, collects answers, scores them client-side, and handles pass/fail + retake.
### Props
```js
<QuizModal
video={activeVideo}
questions={[...]} // array of { question, options, correctIndex }
onSubmit={(score, total) => {}} // called when participant confirms completion
onCancel={() => {}} // close without completing
onRetake={() => {}} // switch to retake question set
/>
```
### State
```js
const [answers, setAnswers] = useState({}); // { [questionIndex]: optionIndex }
const [submitted, setSubmitted] = useState(false);
const [saving, setSaving] = useState(false);
```
### Scoring
```js
const total = questions.length;
const answeredCount = Object.keys(answers).length;
const allAnswered = answeredCount === total;
const score = questions.reduce(
(acc, q, i) => acc + (answers[i] === q.correctIndex ? 1 : 0),
0
);
const passed = total > 0 && score / total >= 0.7;
```
### Submit flow
1. Participant selects answers for all questions. The Submit button is disabled until `allAnswered` is true.
2. On Submit, `submitted` is set to true — the UI now shows correct/incorrect highlighting on each option.
3. If `passed` is true, a "Complete Module" button appears. Clicking it calls `onSubmit(score, total)`, which triggers `markComplete` in the parent.
4. If `passed` is false, a "Retake Quiz" button appears. Clicking it calls `onRetake()`, which sets `isRetake = true` in the parent — the parent re-renders the QuizModal with the retake question set (via the `key` prop change).
### Visual feedback after submit
- Correct option: gold border + gold tint + check icon
- Selected incorrect option: red border + red tint + X icon
- Other options: dimmed
---
## 4. The question bank (hardcoded fallback + database)
Path: `src/components/knowledge/quizQuestions.js`
Two exports provide hardcoded fallback questions:
```js
export const QUIZ_QUESTIONS = {
1: [
{ question: '...', options: ['...', '...', '...', '...'], correctIndex: 0 },
// 3-4 questions per module
],
2: [...],
// ... through module 6
};
export const QUIZ_QUESTIONS_RETAKE = {
1: [...], // alternate questions for retake attempts
// ... through module 6
};
```
**Format:** Each question is `{ question: string, options: string[], correctIndex: number }`.
**Priority:** Database QuizQuestion records (if they exist) take priority over the hardcoded fallback. This lets an admin update questions without a code deploy. The fallback ensures the system works out of the box.
**To add questions via the database:** Create QuizQuestion records with the matching `video_id`, `question`, `options` (array), `correct_index` (0-based), `order`, and `question_set` ('primary' or 'retake'). The admin panel includes a QuizManager component for this.
---
## 5. The admin quiz manager
Path: `src/components/admin/QuizManager.jsx`
Lets an admin create, edit, and delete QuizQuestion records per module, with a toggle between primary and retake sets. Uses the standard entity CRUD SDK:
```js
base44.entities.QuizQuestion.create({ video_id, question, options, correct_index, order, question_set });
base44.entities.QuizQuestion.update(id, { ... });
base44.entities.QuizQuestion.delete(id);
```
---
## 6. How completion triggers the certificate
When a VideoProgress record is marked `completed: true`, a workflow fires:
Path: `base44/workflows/Issue Certificate on Course Completion.jsonc`
```jsonc
{
"name": "Issue Certificate on Course Completion",
"trigger": {
"config": { "trigger_type": "entity", "entity_name": "VideoProgress", "events": ["create", "update"] },
"condition": "${ .trigger.data.completed == true }"
},
"definition": {
"do": [
{
"issue_certificate": {
"call": "invoke_backend_function",
"with": { "function_name": "issue-certificate", "args": { "video_progress_id": "${ .trigger.entity_id }" } },
"then": "end"
}
}
]
}
}
```
The `issue-certificate` backend function checks that ALL modules are complete and ALL quizzes passed (≥70%), then creates an IssuedCertificate record and emails the user. See the Certificate Replication Guide for the full backend function code.
**Idempotency:** The function checks if a certificate already exists for the user before issuing a new one, so it's safe for the workflow to fire on every module completion.
---
## 7. Setup checklist for the new app
1. **Create the entities:** `VideoProgress` and `QuizQuestion` (plus `IssuedCertificate` and `CertificateSettings` if you want certificates — see the Certificate Replication Guide).
2. **Add the components:**
- `src/components/knowledge/VideoSection.jsx` — the video grid + player + quiz trigger
- `src/components/knowledge/QuizModal.jsx` — the quiz overlay
- `src/components/knowledge/quizQuestions.js` — hardcoded fallback questions
- `src/components/admin/QuizManager.jsx` — admin question editor (optional)
3. **Define your modules:** Edit the `VIDEOS` array in `VideoSection.jsx` with your module titles and YouTube video IDs.
4. **Write your quiz questions:** Either edit `quizQuestions.js` directly, or use the QuizManager admin panel to create QuizQuestion records in the database.
5. **Wire up the Knowledge Base page:** Render `<VideoSection />` on your training page (requires the user to be logged in).
6. **Set up certificate issuance (optional):** Create the `issue-certificate` backend function and the workflow — see the Certificate Replication Guide.
7. **Test end-to-end:** Log in as a test user, watch Module 1 to the end, take the quiz, pass it, verify Module 2 unlocks. Repeat through all modules. Verify the certificate appears after the last module.
---
## 8. Customization notes
- **Number of modules:** Change the `VIDEOS` array length. The `completedCount` and `allComplete` logic auto-adjusts. Update `MODULE_VIDEO_IDS` in the `issue-certificate` backend function to match.
- **Pass threshold:** Currently 70% (`score / total >= 0.7`). Change in both `QuizModal.jsx` and the `issue-certificate` backend function.
- **Questions per quiz:** Currently 4. Add or remove questions in `quizQuestions.js` or via the database — the scoring auto-adjusts.
- **Video provider:** Currently YouTube via the IFrame API. To use Vimeo or self-hosted, replace the player creation logic in the `useEffect` that calls `loadYouTubeAPI()`.
- **Sequential vs. free order:** To allow modules in any order, remove the `isUnlocked` check and the `disabled` prop on the module cards.
- **Retake limit:** Currently unlimited. To add a limit, track attempt count in VideoProgress and disable the retake button when exceeded.
- **YouTube blocking:** The video player requires YouTube to be accessible. If your audience may be on a network that blocks YouTube (schools, corporate), consider adding a notice or a fallback video source.
---
## 9. Key files summary
| File | Purpose |
|------|---------|
| `base44/entities/VideoProgress.jsonc` | Entity schema for module completion tracking |
| `base44/entities/QuizQuestion.jsonc` | Entity schema for the question bank |
| `src/components/knowledge/VideoSection.jsx` | Video grid, YouTube player, sequential unlocking, quiz trigger |
| `src/components/knowledge/QuizModal.jsx` | Quiz overlay with scoring, pass/fail, retake |
| `src/components/knowledge/quizQuestions.js` | Hardcoded fallback question banks (primary + retake) |
| `src/components/admin/QuizManager.jsx` | Admin UI for managing database questions |
| `base44/functions/issue-certificate/entry.ts` | Backend function that checks all modules and issues the certificate |
| `base44/workflows/Issue Certificate on Course Completion.jsonc` | Workflow that fires on VideoProgress completion |