File size: 8,532 Bytes
e4ab6d0
0a2fc2b
50fc34c
e4ab6d0
50fc34c
 
 
 
04830fa
50fc34c
 
 
 
04830fa
 
 
50fc34c
 
 
 
 
04830fa
 
 
50fc34c
 
04830fa
50fc34c
 
 
 
04830fa
50fc34c
 
 
 
 
 
 
 
 
 
 
04830fa
50fc34c
04830fa
50fc34c
e4ab6d0
50fc34c
 
 
 
 
 
04830fa
 
e4ab6d0
 
50fc34c
 
 
 
04830fa
50fc34c
 
 
 
04830fa
e4ab6d0
 
50fc34c
 
04830fa
50fc34c
 
 
e4ab6d0
50fc34c
04830fa
50fc34c
 
e4ab6d0
04830fa
e4ab6d0
50fc34c
 
 
 
e4ab6d0
a998588
 
 
 
 
 
e4ab6d0
04830fa
50fc34c
04830fa
e4ab6d0
50fc34c
 
 
 
 
e4ab6d0
50fc34c
 
04830fa
50fc34c
 
 
 
04830fa
50fc34c
 
 
 
 
e4ab6d0
50fc34c
04830fa
 
 
 
 
e4ab6d0
 
50fc34c
 
6f70a09
50fc34c
 
6f70a09
e4ab6d0
04830fa
 
 
e4ab6d0
 
6f70a09
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
<div class="reading-container">
  <app-header [title]="'Reading'"></app-header>
  <img src="assets/images/grammar-bg.png" alt="Background" class="grammar-bg" />

  <div class="main-container" *ngIf="!showCongrats">
    <section class="intro-section split" *ngIf="!content && !hasStarted">
      <div class="split-shell">
        <div class="split-left">
          <img class="intro-illustration" src="assets/images/reading/teacher.png" alt="Reading and quiz illustration" />
        </div>
        <div class="split-right">
          <h1 class="hero-title">Welcome to the Reading Exercise</h1>
          <p class="hero-copy">
            The Reading component is a simple tool that turns any meaningful topic into a short, age-appropriate passage. It checks the topic first to avoid nonsense and unsafe inputs, then creates clear content at the chosen level (Easy, Medium, or Hard). After
            reading, it generates multiple-choice questions and gives instant feedback. Read-Aloud and A−/A+ controls support different learning needs. This helps students build comprehension and vocabulary, and saves teachers and parents
            time during practice, homework, and revision.
          </p>
          <div class="form-row">
            <label class="row-label">Topic:</label>
            <div class="input-wrap clearable" [class.locked]="!difficulty">
              <span class="icon-search" aria-hidden="true"></span>
              <input type="text" class="has-clear" [(ngModel)]="topic" [placeholder]="difficulty ? 'Enter or select a topic' : 'Select a level first'" [disabled]="!difficulty" (focus)="openSuggestions()" (input)="onTyping()" (keydown)="onKeydown($event)" (blur)="hideSuggestionsWithDelay()"
                     autocomplete="off" aria-autocomplete="list" [attr.aria-expanded]="showSuggestions && !!difficulty" [attr.aria-disabled]="!difficulty" />
              <button class="clear-btn" *ngIf="difficulty && topic.trim().length" (mousedown)="$event.preventDefault()" (click)="onClearTopic()">×</button>
              <div class="suggestion-box" *ngIf="difficulty && showSuggestions">
                <ng-container *ngIf="filteredSuggestions?.length">
                  <span *ngFor="let s of filteredSuggestions; let i = index" (mousedown)="selectSuggestion(s)" [class.active]="i === activeIndex">{{ s }}</span>
                </ng-container>
              </div>
            </div>
            <div class="field-hint" *ngIf="!difficulty">
              <span class="lock-icon" aria-hidden="true"></span> Please select a level to enable topic suggestions.
            </div>
          </div>
          <div class="form-row">
            <label class="row-label">Select Level:</label>
            <div class="select-wrap">
              <span class="icon-level" aria-hidden="true"></span>
              <select [(ngModel)]="difficulty" (ngModelChange)="onDifficultyChange($event)" required>
                <option [ngValue]="'easy'">Easy</option>
                <option [ngValue]="'medium'">Medium</option>
                <option [ngValue]="'hard'">Hard</option>
              </select>
              <span class="badge" *ngIf="difficulty" [attr.data-level]="difficulty">{{ difficulty | titlecase }}</span>
            </div>
            <app-button (click)="generateContent()" [disabled]="!topic.trim() || !difficulty.trim() || isGenerateDisabled">Generate Passage</app-button>
          </div>
        </div>
      </div>
      <div class="loader-overlay" *ngIf="isGeneratingContent" role="status" aria-live="polite">
        <div class="loader">Loading</div>
      </div>
      <div class="popup-overlay" *ngIf="showPopup">
        <div class="popup-content">
          <p>{{ errorMessage || 'We could not create the passage right now. Please try again.' }}</p>
          <app-button (click)="closeErrorPopup()">Close</app-button>
        </div>
      </div>
    </section>

    <div *ngIf="content && !hasStarted" class="reading-card">
      <div class="reading-head">
        <img src="assets/images/reading/back.png" (click)="goToIntroSection()" alt="" class="icon-img" />
        <h2 class="reading-title">Let’s Start Reading!</h2>
        <div class="head-actions">
          <button class="icon-btn" (click)="decreaseFont()" aria-label="Decrease font">A−</button>
          <button class="icon-btn" (click)="increaseFont()" aria-label="Increase font">A+</button>
          <button class="icon-btn" [class.active]="isReading || ttsPaused" (click)="toggleReadAloud()" [attr.aria-pressed]="isReading || ttsPaused" aria-label="Read aloud">{{ isReading ? '⏸' : '🔊' }}</button>
        </div>
      </div>
      <div class="reading-meta">
        <span class="chip chip-topic" *ngIf="normalizedTopic || topic">📚 {{ normalizedTopic || topic }}</span>
        <span class="chip chip-level" [attr.data-level]="difficulty">{{ difficulty | titlecase }}</span>
      </div>
      <div class="passage-shell">
        <div class="passage-text" [innerHTML]="transformContent(content)"></div>
      </div>
      <div class="reading-actions">
        <app-button (click)="stopReadAloud(); generateQuestions()" [disabled]="isGenerateQuestionDisabled">Generate Questions</app-button>
        <div class="loader-overlay" *ngIf="loadingQuestions" aria-live="polite" aria-busy="true">
          <span class="loader">Loading</span>
        </div>
        <app-button (click)="stopReadAloud(); resetAll()">Reset</app-button>
      </div>
    </div>

    <div class="mcq-card" *ngIf="hasStarted && questions?.length">
      <div class="mcq-card__header">

        <img src="assets/images/reading/back.png"
             alt="Back"
             class="icon-img"
             (click)="goToReadingPassage()"
             tabindex="0"
             (keydown.enter)="goToReadingPassage()" />

        <h3 class="mcq-card__title">Question {{ currentQuestionIndex + 1 }} of {{ questions.length }}</h3>
        <div class="mcq-card__actions">
          <button class="user-guide-close-icon" (click)="startOver()">×</button>
        </div>
      </div>
      <div class="mcq-card__body">
        <div class="quiz-pill quiz-question-pill">
          <span class="qq-label">Question:</span>
          <span class="qq-text">{{ questions[currentQuestionIndex].question }}</span>
        </div>
        <ul class="quiz-options">
          <li *ngFor="let option of (questions[currentQuestionIndex]?.options | slice:0:4); let i = index">
            <label class="quiz-pill quiz-option-pill" [ngClass]="{
                'is-selected': !questions[currentQuestionIndex].isChecked && getSelectedAnswer() === option,
                'is-correct':  questions[currentQuestionIndex].isChecked && questions[currentQuestionIndex].correct_answer === option,
                'is-incorrect':questions[currentQuestionIndex].isChecked && getSelectedAnswer() === option && option !== questions[currentQuestionIndex].correct_answer
              }">
              <input type="radio" class="visually-hidden" [name]="'q_'+currentQuestionIndex" [value]="option" [checked]="getSelectedAnswer() === option" (change)="setSelectedAnswer(option)" [disabled]="questions[currentQuestionIndex].isChecked" />
              <span class="slot">{{ ['A','B','C','D'][i] }}:</span>
              <span class="opt-text">{{ option }}</span>
            </label>
          </li>
        </ul>
      </div>
      <div class="mcq-card__footer">
        <app-button *ngIf="currentQuestionIndex > 0" (click)="previousQuestion()">◀ Previous</app-button>
        <div style="display: flex; gap: 10px; justify-content: flex-end; flex: 1;">
          <app-button *ngIf="!questions[currentQuestionIndex].isChecked" (click)="validateAnswer(); scheduleCongratsIfLast()" [disabled]="!selectedAnswers[questions[currentQuestionIndex].question]">Validate</app-button>
          <app-button *ngIf="questions[currentQuestionIndex].isChecked && currentQuestionIndex < questions.length - 1" (click)="nextQuestion()">Next ▶</app-button>
        </div>
      </div>
    </div>
  </div>

  <div class="congrats-overlay" *ngIf="showCongrats" aria-live="polite" aria-modal="true" role="dialog">
    <div class="congrats-card">
      <div class="score-badge" aria-label="Your score">
        Your Score: <span class="score">{{ scoreCorrect }}</span> / <span class="total">{{ scoreTotal }}</span>
      </div>
      <h2>{{ congratsTitle }}</h2>
      <p>{{ congratsMessage }}</p>
      <app-button (click)="startOver()">Start Over</app-button>
    </div>
  </div>
</div>