Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions openspec/changes/add-question-of-the-day/.openspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-08-27
40 changes: 40 additions & 0 deletions openspec/changes/add-question-of-the-day/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
## Context

The application introduces the "Question of the Day" (QOTD) feature to enhance student daily retention. We utilize the `/api/v2.4/daily_questions/` JSON endpoint to power a native interactive quiz workflow.

## Goals / Non-Goals

**Goals:**
- Update `InstituteSettings` to parse and store `qotdEnabled`.
- Fetch and dynamically parse daily questions data with support for polymorphic subject/difficulty payloads.
- Provide a dual-screen flow: `QotdOverviewScreen` (summary & statistics) and `QotdQuizScreen` (interactive stepper quiz).
- Use `AppHtmlV2` with MathJax SVG rendering support.

**Non-Goals:**
- Using a WebView.
- Offline persistence via Drift (QOTD is time-sensitive and daily-scoped, requiring live backend validation).

## Decisions

**1. Native Interactive Quiz Architecture with Riverpod**
- **Rationale**: Manages question navigation, option selection, optimistic submissions, and solution state via `QotdQuizController`.

**2. Robust Dynamic Subject Parsing in `QotdDto`**
- **Rationale**: The backend returns subjects in varied shapes (flat strings, `{name: ...}` maps, lists, category tags). A dynamic extractor handles all shapes without artificial hardcoded fallbacks.

**3. Streamlined Question Header & Unified Metadata Pill**
- **Rationale**: Placing progress only in `AppHeader` and grouping `[Subject • Difficulty • Type]` into a single pill with `#0F172A` high-contrast text removes UI clutter while improving scannability.

**4. `AppHtmlV2` MathJax SVG Rendering**
- **Rationale**: Decodes and renders custom MathJax SVGs natively within the HTML flow.

**5. Direct Online-Only Repository (`QotdRepository`)**
- **Rationale**: QOTD features are inherently time-sensitive and daily-scoped. Submissions and daily resets require live server validation, making local Drift cache synchronization unnecessary and intentionally omitted.

**6. Conditional Cache Invalidation on Quiz Exit**
- **Rationale**: Tracking `hasSubmittedNewAnswer` avoids redundant network calls and loading states when users are simply viewing solutions or navigating without answering, ensuring zero-latency return transitions to the overview.

## Risks / Trade-offs

- **Risk: Varying API Payload Structures** → Mitigation: Dynamic extractor in `QotdDto` ensures resilient parsing across different question serializers.
- **Trade-off: No Offline Persistence** → Mitigation: Daily questions rely on real-time server evaluations; network loading states are gracefully covered by structured shimmer skeletons.
27 changes: 27 additions & 0 deletions openspec/changes/add-question-of-the-day/proposal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
## Why

We need to add the "Question of the Day" (QOTD) feature to the application. This feature increases daily student engagement by providing a quick, daily learning activity accessible directly from the dashboard navigation with an interactive quiz experience, progress tracking, and detailed explanations.

## What Changes

- Add boolean flag `qotdEnabled` to the `InstituteSettings` data model.
- Add a new "Daily Questions" menu item in the Dashboard Drawer.
- Introduce native `QotdOverviewScreen` and `QotdQuizScreen` driven by Riverpod (`qotdQuizControllerProvider`).
- Implement dynamic `QotdDto` parsing for subject, difficulty, question type, options, and past attempts.
- Render questions using `AppHtmlV2` with custom MathJax SVG decoding support.

## Capabilities

### New Capabilities
- `daily-questions`: Feature providing landing overview, interactive multi-question quiz flow, option selection, attempt submission, and solution explanations.

### Modified Capabilities
- `lms-home-paid-active`: Adding the "Daily Questions" item to the drawer navigation.

## Impact

- `packages/core/lib/data/config/institute_settings.dart`: Modified to support the new `qotd_enabled` JSON field.
- `packages/core/lib/data/models/qotd_dto.dart`: Robust DTO parsing subject, difficulty, type, options, and attempts.
- `packages/core/lib/widgets/app_html_v2.dart`: Custom MathJax SVG decoding.
- `packages/testpress/lib/screens/dashboard/qotd/`: Contains `QotdOverviewScreen`, `QotdQuizScreen`, controller, and widgets.
- `packages/testpress/lib/navigation/app_router.dart`: Added routing for the `/qotd` screen.
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
## ADDED Requirements

### Requirement: Daily Questions Overview and Interactive Quiz
The system SHALL provide dedicated native screens to display the Question of the Day overview and interactive quiz stepper.

#### Scenario: Overview Screen
- **WHEN** the user navigates to `/qotd`
- **THEN** the system MUST display the daily questions overview screen with the completion gauge, date, and CTA to start/resume or view solutions.

#### Scenario: Interactive Quiz Stepper
- **WHEN** the user starts the quiz
- **THEN** the system MUST render questions using `AppHtmlV2` with MathJax SVG decoding.
- **AND** it MUST show a unified question metadata pill (`[Subject • Difficulty • Type]`) above the question text.
- **AND** it MUST display the single question progress in `AppHeader` with a top-aligned back button.

#### Scenario: Dynamic Data Model Parsing
- **WHEN** `QotdDto` parses question payloads from the API
- **THEN** it MUST dynamically extract subject, difficulty, and question type without hardcoding defaults.
- **AND** `InstituteSettings` MUST correctly parse the `qotd_enabled` boolean field.

### Requirement: Online-Only Repository Operations
The system SHALL query daily questions and submit answers directly against the network API without local Drift caching, ensuring daily reset states and answer evaluations are strictly governed by the backend.

#### Scenario: Real-Time Network State
- **WHEN** the user opens the overview or submits an answer
- **THEN** `QotdRepository` MUST communicate directly with `DataSource` without persisting attempts to local database tables.

#### Scenario: Solution Review Navigation
- **WHEN** the user closes the quiz after reviewing solutions without submitting new answers
- **THEN** the system MUST return immediately to the overview without invalidating provider cache or making network calls.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
## MODIFIED Requirements

### Requirement: Top App Bar & Navigation Access
The system SHALL provide a persistent top app bar (DashboardHeader) with access to global navigation options, including the navigation drawer.

#### Scenario: Hamburger Menu Placeholder
- **WHEN** the `DashboardHeader` is rendered
- **THEN** it MUST include a hamburger menu icon (`Icons.menu_rounded`)
- **AND** the actual menu sidebar functionality is deferred to a separate/future spec change.

#### Scenario: Daily Questions Drawer Item
- **GIVEN** `instituteSettings.qotdEnabled` is `true`
- **WHEN** the `DashboardDrawer` is rendered
- **THEN** it MUST include a "Daily Questions" item with a calendar icon
- **AND** tapping it MUST route the user to the `/qotd` screen.
20 changes: 20 additions & 0 deletions openspec/changes/add-question-of-the-day/tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
## 1. Data Model & Architecture
- [x] 1.1 Update `InstituteSettings` model to include `final bool qotdEnabled;`
- [x] 1.2 Update `fromJson` and `toJson` serialization logic for `qotd_enabled`
- [x] 1.3 Create `QotdDto`, `QotdOptionDto`, `QotdSubmitResponseDto`, and `QotdSummaryDto`
- [x] 1.4 Implement dynamic `extractSubject` parser in `QotdDto` to handle polymorphic API shapes without hardcoding
- [x] 1.5 Implement `QotdRepository` and `qotdQuizControllerProvider` (Riverpod)

## 2. Screens & UI Flow
- [x] 2.1 Implement `QotdOverviewScreen` with statistics gauge, completion summary, and date tracking
- [x] 2.2 Implement interactive `QotdQuizScreen` with multi-question stepper and solution view
- [x] 2.3 Configure `/qotd` and quiz routes in `AppRouter`
- [x] 2.4 Add "Daily Questions" item with calendar icon to `DashboardDrawer` (guarded by `qotdEnabled`)

## 3. Visual Polish & Design System Governance
- [x] 3.1 Single unified metadata pill (`Subject • Difficulty • Type`) using design tokens and `#0F172A` text
- [x] 3.2 Streamlined `AppHeader` with top-aligned back button and progress indicator
- [x] 3.3 Remove redundant question labels and sub-prompts
- [x] 3.4 Support custom MathJax SVG rendering in `AppHtmlV2`


1 change: 1 addition & 0 deletions packages/core/lib/core.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export 'widgets/app_toast.dart';
export 'widgets/session_expired_dialog.dart';
export 'widgets/app_confirmation_dialog.dart';
export 'widgets/ai_composer.dart';
export 'widgets/donut_chart.dart';

// Shell
export 'shell/app_shell.dart';
Expand Down
4 changes: 4 additions & 0 deletions packages/core/lib/data/config/institute_settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ class InstituteSettings {
final String? learnlensOrgID;

final bool disableStudentReport;
final bool qotdEnabled;

const InstituteSettings({
required this.domainUrl,
Expand Down Expand Up @@ -116,6 +117,7 @@ class InstituteSettings {
required this.currentPaymentApp,
required this.learnlensEnabled,
required this.disableStudentReport,
required this.qotdEnabled,
this.learnlensOrgID,
this.videoWatermarkType,
this.videoWatermarkPosition,
Expand Down Expand Up @@ -190,6 +192,7 @@ class InstituteSettings {
learnlensEnabled: json['learnlens_enabled'] as bool? ?? false,
learnlensOrgID: json['learnlens_organization_id'] as String?,
disableStudentReport: json['disable_student_report'] as bool? ?? false,
qotdEnabled: json['qotd_enabled'] as bool? ?? false,

videoWatermarkType: switch (watermarkType) {
'dynamic' => VideoWatermarkType.dynamic,
Expand Down Expand Up @@ -256,6 +259,7 @@ class InstituteSettings {
'learnlens_enabled': learnlensEnabled,
'learnlens_organization_id': learnlensOrgID,
'disable_student_report': disableStudentReport,
'qotd_enabled': qotdEnabled,
'video_watermark_type': switch (videoWatermarkType) {
VideoWatermarkType.dynamic => 'dynamic',
VideoWatermarkType.static => 'static',
Expand Down
3 changes: 3 additions & 0 deletions packages/core/lib/data/data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export 'models/quiz_review_result_dto.dart';
export 'models/custom_test_config_dto.dart';
export 'models/custom_exam_generation_dto.dart';
export 'models/learnlens_dto.dart';
export 'models/qotd_dto.dart';

// Database
export 'db/tables/dashboard_tables.dart';
Expand Down Expand Up @@ -75,10 +76,12 @@ export 'repositories/doubt_repository.dart';
export 'repositories/live_classes_repository.dart';
export 'repositories/repository_providers.dart';
export 'repositories/institute_settings_repository.dart';
export 'repositories/qotd_repository.dart';
export 'providers/bookmark_provider.dart';
export 'providers/announcements_provider.dart';
export 'providers/institute_settings_provider.dart';
export 'providers/doubt_providers.dart';
export 'providers/qotd_provider.dart';

//service
export 'services/downloads_service.dart';
Expand Down
Loading