firka: show ghost grades on the chart and in the grades list

This commit is contained in:
2026-03-02 21:40:01 +01:00
parent 68ddffd808
commit d96c4b66bb
4 changed files with 93 additions and 11 deletions

View File

@@ -986,8 +986,9 @@ Future<void> showHomeworkBottomSheet(
Future<void> showGradeCalculatorBottomSheet( Future<void> showGradeCalculatorBottomSheet(
BuildContext context, BuildContext context,
AppInitialization data, AppInitialization data,
Subject subject, Subject subject, {
) async { void Function(int grade, int weight)? onAdd,
}) async {
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
elevation: 100, elevation: 100,
@@ -1017,6 +1018,7 @@ Future<void> showGradeCalculatorBottomSheet(
child: _GradeCalculatorSheetContent( child: _GradeCalculatorSheetContent(
data: data, data: data,
subject: subject, subject: subject,
onAdd: onAdd,
), ),
), ),
), ),
@@ -1030,10 +1032,12 @@ Future<void> showGradeCalculatorBottomSheet(
class _GradeCalculatorSheetContent extends StatefulWidget { class _GradeCalculatorSheetContent extends StatefulWidget {
final AppInitialization data; final AppInitialization data;
final Subject subject; final Subject subject;
final void Function(int grade, int weight)? onAdd;
const _GradeCalculatorSheetContent({ const _GradeCalculatorSheetContent({
required this.data, required this.data,
required this.subject, required this.subject,
this.onAdd,
}); });
@override @override
@@ -1193,9 +1197,9 @@ class _GradeCalculatorSheetContentState
), ),
child: Slider( child: Slider(
value: weightPercent.toDouble(), value: weightPercent.toDouble(),
min: 0, min: 1,
max: 100, max: 500,
divisions: 100, divisions: 499,
onChanged: (v) => setState(() => weightPercent = v.round()), onChanged: (v) => setState(() => weightPercent = v.round()),
), ),
), ),
@@ -1203,7 +1207,7 @@ class _GradeCalculatorSheetContentState
), ),
SizedBox(width: 12), SizedBox(width: 12),
SizedBox( SizedBox(
width: 48, width: 56,
child: Text( child: Text(
'$weightPercent%', '$weightPercent%',
style: appStyle.fonts.B_16R.apply( style: appStyle.fonts.B_16R.apply(
@@ -1231,6 +1235,7 @@ class _GradeCalculatorSheetContentState
setState(() { setState(() {
entries.add((selectedGrade, weightPercent)); entries.add((selectedGrade, weightPercent));
}); });
widget.onAdd?.call(selectedGrade, weightPercent);
}, },
child: Text( child: Text(
widget.data.l10n.grade_calculator_add, widget.data.l10n.grade_calculator_add,
@@ -1257,8 +1262,9 @@ class _GradeCalculatorSheetContentState
Future<void> showSubjectBottomSheetSettings( Future<void> showSubjectBottomSheetSettings(
BuildContext context, BuildContext context,
AppInitialization data, AppInitialization data,
Subject subject, Subject subject, {
) async { void Function(int grade, int weight)? onAddFromCalculator,
}) async {
final parentContext = context; final parentContext = context;
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
@@ -1315,6 +1321,7 @@ Future<void> showSubjectBottomSheetSettings(
parentContext, parentContext,
data, data,
subject, subject,
onAdd: onAddFromCalculator,
); );
}, },
child: Container( child: Container(

View File

@@ -29,6 +29,7 @@ class HomeGradesSubjectScreen extends StatefulWidget {
class _HomeGradesSubjectScreen extends FirkaState<HomeGradesSubjectScreen> { class _HomeGradesSubjectScreen extends FirkaState<HomeGradesSubjectScreen> {
Iterable<Grade>? grades; Iterable<Grade>? grades;
final List<(int grade, int weight)> _ghostEntries = [];
void _onRefreshRequested(BuildContext context) async { void _onRefreshRequested(BuildContext context) async {
final cubit = context.read<HomeRefreshCubit>(); final cubit = context.read<HomeRefreshCubit>();
@@ -55,6 +56,41 @@ class _HomeGradesSubjectScreen extends FirkaState<HomeGradesSubjectScreen> {
})(); })();
} }
List<Grade> _gradesWithGhosts(Subject subject) {
final real = grades?.toList() ?? [];
if (_ghostEntries.isEmpty) return real;
final baseDate = real.isEmpty
? DateTime.now()
: real
.map((g) => g.creationDate)
.reduce((a, b) => a.isAfter(b) ? a : b);
final osztalyzat = NameUidDesc(
uid: '1,Osztalyzat',
name: 'Osztalyzat',
description: '',
);
final ghostGrades = <Grade>[];
for (var i = 0; i < _ghostEntries.length; i++) {
final e = _ghostEntries[i];
ghostGrades.add(
Grade(
uid: 'ghost-$i-${e.$1}-${e.$2}',
recordDate: baseDate.add(Duration(seconds: i)),
creationDate: baseDate.add(Duration(seconds: i)),
subject: subject,
type: osztalyzat,
valueType: osztalyzat,
teacher: '',
strValue: '${e.$1}',
sortIndex: 0,
numericValue: e.$1,
weightPercentage: e.$2,
),
);
}
return [...real, ...ghostGrades];
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocListener<HomeRefreshCubit, HomeRefreshState>( return BlocListener<HomeRefreshCubit, HomeRefreshState>(
@@ -72,7 +108,30 @@ class _HomeGradesSubjectScreen extends FirkaState<HomeGradesSubjectScreen> {
var aGrade = grades!.first; var aGrade = grades!.first;
var groups = grades!.groupList((grade) => grade.recordDate); var groups = grades!.groupList((grade) => grade.recordDate);
final ghostGradeWidgets = _ghostEntries.reversed.map((e) {
return GestureDetector(
child: FirkaCard(
left: [
Row(
children: [
GradeWidget.gradeValue(e.$1),
SizedBox(width: 8),
Text(
'${widget.data.l10n.ghost_grade} ${e.$2}%',
style: appStyle.fonts.B_16SB.apply(
color: appStyle.colors.textPrimary,
),
),
],
),
],
),
onTap: () {},
);
}).toList();
var gradeWidgets = List<Widget>.empty(growable: true); var gradeWidgets = List<Widget>.empty(growable: true);
gradeWidgets.addAll(ghostGradeWidgets);
for (var group in groups.entries) { for (var group in groups.entries) {
gradeWidgets.add(SizedBox(height: 8)); gradeWidgets.add(SizedBox(height: 8));
@@ -190,6 +249,9 @@ class _HomeGradesSubjectScreen extends FirkaState<HomeGradesSubjectScreen> {
context, context,
widget.data, widget.data,
aGrade.subject, aGrade.subject,
onAddFromCalculator: (g, w) {
setState(() => _ghostEntries.add((g, w)));
},
); );
}, },
), ),
@@ -237,7 +299,9 @@ class _HomeGradesSubjectScreen extends FirkaState<HomeGradesSubjectScreen> {
SizedBox(height: 15), SizedBox(height: 15),
], ],
), ),
GradeChartWithInteraction(grades: grades?.toList() ?? []), GradeChartWithInteraction(
grades: _gradesWithGhosts(aGrade.subject),
),
SizedBox(height: 12), SizedBox(height: 12),
Padding( Padding(
padding: EdgeInsets.only(left: 4), padding: EdgeInsets.only(left: 4),

View File

@@ -26,7 +26,7 @@ class _GradeChartState extends State<GradeChart> {
appStyle.colors.grade1, appStyle.colors.grade1,
]; ];
late final List<FlSpot> spots; late List<FlSpot> spots;
double? _subjectAverageInList(List<Grade> grades, String subjectUid) { double? _subjectAverageInList(List<Grade> grades, String subjectUid) {
double weightedSum = 0; double weightedSum = 0;
@@ -67,7 +67,10 @@ class _GradeChartState extends State<GradeChart> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_computeSpots();
}
void _computeSpots() {
final sortedGrades = List<Grade>.from(widget.grades) final sortedGrades = List<Grade>.from(widget.grades)
..sort((a, b) => a.creationDate.compareTo(b.creationDate)); ..sort((a, b) => a.creationDate.compareTo(b.creationDate));
@@ -90,6 +93,14 @@ class _GradeChartState extends State<GradeChart> {
} }
} }
@override
void didUpdateWidget(covariant GradeChart oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.grades.length != widget.grades.length) {
_computeSpots();
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ClipRRect( return ClipRRect(