104 lines
3.3 KiB
Dart
104 lines
3.3 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
import 'package:syncfusion_flutter_charts/charts.dart';
|
|
|
|
import 'package:pshared/models/payment/operation.dart';
|
|
|
|
import 'package:pweb/generated/i18n/app_localizations.dart';
|
|
|
|
|
|
class PayoutDistributionChart extends StatelessWidget {
|
|
final List<OperationItem> operations;
|
|
const PayoutDistributionChart({super.key, required this.operations});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
// 1) Aggregate sums
|
|
final sums = <String, double>{};
|
|
for (var op in operations) {
|
|
final name = op.name;
|
|
sums[name] = (sums[name] ?? 0) + op.amount;
|
|
}
|
|
if (sums.isEmpty) {
|
|
return Center(child: Text(AppLocalizations.of(context)!.noPayouts));
|
|
}
|
|
|
|
// 2) Build chart data
|
|
final data = sums.entries
|
|
.map((e) => _ChartData(e.key, e.value))
|
|
.toList();
|
|
|
|
// 3) Build a simple horizontal legend
|
|
final palette = [
|
|
Theme.of(context).colorScheme.primary,
|
|
Theme.of(context).colorScheme.secondary,
|
|
Theme.of(context).colorScheme.tertiary,
|
|
Theme.of(context).colorScheme.primaryContainer,
|
|
Theme.of(context).colorScheme.secondaryContainer,
|
|
];
|
|
final legendItems = List<Widget>.generate(data.length, (i) {
|
|
return Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Icon(Icons.circle, size: 10, color: palette[i % palette.length]),
|
|
const SizedBox(width: 4),
|
|
Text(data[i].label, style: Theme.of(context).textTheme.bodySmall),
|
|
if (i < data.length - 1) const SizedBox(width: 12),
|
|
],
|
|
);
|
|
});
|
|
|
|
return Card(
|
|
margin: const EdgeInsets.all(16),
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
|
elevation: 2,
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(16),
|
|
child: Row(
|
|
children: [
|
|
// Pie takes 2/3 of the width
|
|
Expanded(
|
|
flex: 2,
|
|
child: SfCircularChart(
|
|
legend: Legend(isVisible: false),
|
|
tooltipBehavior: TooltipBehavior(enable: true),
|
|
series: <PieSeries<_ChartData, String>>[
|
|
PieSeries<_ChartData, String>(
|
|
dataSource: data,
|
|
xValueMapper: (d, _) => d.label,
|
|
yValueMapper: (d, _) => d.value,
|
|
dataLabelMapper: (d, _) =>
|
|
'${(d.value / sums.values.fold(0, (a, b) => a + b) * 100).toStringAsFixed(1)}%',
|
|
dataLabelSettings: const DataLabelSettings(
|
|
isVisible: true,
|
|
labelPosition: ChartDataLabelPosition.inside,
|
|
),
|
|
radius: '100%',
|
|
)
|
|
],
|
|
),
|
|
),
|
|
|
|
const SizedBox(width: 16),
|
|
|
|
// Legend takes 1/3
|
|
Expanded(
|
|
flex: 1,
|
|
child: SingleChildScrollView(
|
|
scrollDirection: Axis.horizontal,
|
|
child: Column(spacing: 4.0, mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: legendItems),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _ChartData {
|
|
final String label;
|
|
final double value;
|
|
_ChartData(this.label, this.value);
|
|
}
|