Skip to main content

Evaluation Mode

Overview

When evaluationMode=true is set at the school level, all teacher modules except Exam Management become read-only. This allows teachers to focus solely on exam operations during evaluation periods without modifying other records.

Module Impact

ModuleReadWriteNotes
AnnouncementRead-only
AssignmentRead-only
AttendanceCannot mark attendance
Class ManagementRead-only
CompetitionRead-only
EventsRead-only
ExamFull write access
FeeRead-only
LeaveRead-only
ResultsRead-only (exam results managed via Exam)
SubjectsRead-only
TeachersRead-only
Time TableRead-only
MessagingRead-only

Implementation

Server-Side Enforcement

The NestJS API checks school.evaluationMode before allowing write operations:

@Injectable()
export class EvaluationModeGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const school = getCurrentSchool(); // from JWT schoolId
if (school.evaluationMode && !isExamModule(context.getClass())) {
throw new ForbiddenException('EVALUATION_MODE_READONLY');
}
return true;
}
}

App-Side Handling — EvaluationInterceptor

// dio_client.dart
class EvaluationInterceptor extends Interceptor {
@override
void onError(DioException err, ErrorInterceptorHandler handler) {
if (err.response?.statusCode == 403 &&
err.response?.data['error'] == 'EVALUATION_MODE_READONLY') {
// Emit event to trigger permission sync
EventBus.instance.emit('EVALUATION_MODE_TRIGGERED');
// Show snackbar
Get.snackbar(
'Evaluation Mode',
'Write operations are disabled during the evaluation period.',
snackPosition: SnackPosition.BOTTOM,
);
}
handler.next(err);
}
}

Permission Re-Sync

// auth_viewmodel.dart
class AuthViewModel extends BaseProvider {
Future<void> syncPermissions() async {
try {
final response = await _authService.getPermissions();
_permissions = response.data['permissions'];
// Persist to shared_preferences for offline access
await storage.write('permissions', jsonEncode(_permissions));
} catch (e) {
setError(e.toString());
}
}
}

// Called when EvaluationInterceptor emits event
EventBus.instance.on('EVALUATION_MODE_TRIGGERED', (_) {
context.read<AuthViewModel>().syncPermissions();
});

UI Disable Pattern

Each ViewModel exposes a canWrite check:

bool get canWriteAttendance {
final hasWrite = _permissions.any(
(p) => p['moduleName'] == 'attendance' && p['canWrite'] == true,
);
return hasWrite && !_isEvaluationMode;
}

bool get isEvaluationMode {
// Fetched from school config on login
return _authService.isEvaluationMode;
}

Write buttons are conditionally rendered:

ElevatedButton(
onPressed: vm.canWriteAttendance
? () => _submitAttendance()
: null, // Disabled in evaluation mode
child: const Text('Submit Attendance'),
)

Detecting Evaluation Mode

Evaluation mode is detected:

  1. On 403 responseEvaluationInterceptor catches EVALUATION_MODE_READONLY
  2. On app lifecycle resumeAppLifecycleObserver re-fetches permissions from server
  3. On login — Permissions returned in auth response include evaluationMode flag