feat(app): add evaluation history, bot control panel, simulation visualizer, and vendored signalr_core

This commit is contained in:
2026-08-24 21:37:43 +02:00
parent 676496b77d
commit 0894c40f07
113 changed files with 12413 additions and 3613 deletions
@@ -0,0 +1,34 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import '../repositories/admin_repository.dart';
import 'admin_evaluation_history_event.dart';
import 'admin_evaluation_history_state.dart';
export 'admin_evaluation_history_event.dart';
export 'admin_evaluation_history_state.dart';
class AdminEvaluationHistoryBloc extends Bloc<AdminEvaluationHistoryEvent, AdminEvaluationHistoryState> {
final AdminRepository repository;
AdminEvaluationHistoryBloc({required this.repository}) : super(AdminEvaluationHistoryInitial()) {
on<FetchEvaluationHistory>(_onFetch);
}
Future<void> _onFetch(FetchEvaluationHistory event, Emitter<AdminEvaluationHistoryState> emit) async {
emit(AdminEvaluationHistoryLoading());
try {
final response = await repository.fetchEvaluationHistory(
fromUtc: event.fromUtc,
toUtc: event.toUtc,
outcome: event.outcome,
triggerSource: event.triggerSource,
search: event.search,
page: event.page,
pageSize: event.pageSize,
);
emit(AdminEvaluationHistoryLoaded(response: response, page: event.page, pageSize: event.pageSize));
} catch (e) {
emit(AdminEvaluationHistoryError(e.toString().replaceFirst('Exception: ', '')));
}
}
}
@@ -0,0 +1,36 @@
import 'package:equatable/equatable.dart';
import '../models/evaluation_history_enums.dart';
abstract class AdminEvaluationHistoryEvent extends Equatable {
const AdminEvaluationHistoryEvent();
@override
List<Object?> get props => [];
}
/// Fetches (or re-fetches) one page of evaluation history for the given filter
/// set. There is deliberately no separate "change page" event — every fetch is
/// a full filter snapshot, so the bloc never has to guess which filters were
/// active on a previously-loaded page when the caller asks for the next one.
class FetchEvaluationHistory extends AdminEvaluationHistoryEvent {
final DateTime? fromUtc;
final DateTime? toUtc;
final OutcomeReason? outcome;
final TriggerSource? triggerSource;
final String? search;
final int page;
final int pageSize;
const FetchEvaluationHistory({
this.fromUtc,
this.toUtc,
this.outcome,
this.triggerSource,
this.search,
this.page = 1,
this.pageSize = 50,
});
@override
List<Object?> get props => [fromUtc, toUtc, outcome, triggerSource, search, page, pageSize];
}
@@ -0,0 +1,48 @@
import 'package:equatable/equatable.dart';
import '../models/evaluation_history_response_model.dart';
abstract class AdminEvaluationHistoryState extends Equatable {
const AdminEvaluationHistoryState();
@override
List<Object?> get props => [];
}
class AdminEvaluationHistoryInitial extends AdminEvaluationHistoryState {}
class AdminEvaluationHistoryLoading extends AdminEvaluationHistoryState {}
class AdminEvaluationHistoryLoaded extends AdminEvaluationHistoryState {
final EvaluationHistoryResponseModel response;
final int page;
final int pageSize;
const AdminEvaluationHistoryLoaded({
required this.response,
required this.page,
required this.pageSize,
});
bool get hasPreviousPage => page > 1;
bool get hasNextPage => page * pageSize < response.totalCount;
int get rangeStart => response.totalCount == 0 ? 0 : (page - 1) * pageSize + 1;
int get rangeEnd {
final end = page * pageSize;
return end > response.totalCount ? response.totalCount : end;
}
@override
List<Object?> get props => [response, page, pageSize];
}
class AdminEvaluationHistoryError extends AdminEvaluationHistoryState {
final String message;
const AdminEvaluationHistoryError(this.message);
@override
List<Object?> get props => [message];
}