130 lines
4.2 KiB
Dart
130 lines
4.2 KiB
Dart
/// Refine a single pile — promote / demote cards (Important Cards pattern).
|
|
library;
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:provider/provider.dart';
|
|
|
|
import '../models/value_pile.dart';
|
|
import '../providers/values_sort_provider.dart';
|
|
import '../widgets/practice_chrome.dart';
|
|
import '../widgets/value_card_tile.dart';
|
|
import '../utils/app_theme.dart';
|
|
|
|
class PileReviewScreen extends StatelessWidget {
|
|
const PileReviewScreen({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final sort = context.watch<ValuesSortController>();
|
|
final pile = sort.reviewPile ?? ValuePile.important;
|
|
final cards = sort.cardsInPile(pile);
|
|
final text = Theme.of(context).textTheme;
|
|
final colors = Theme.of(context).colorScheme;
|
|
|
|
return PracticeScaffold(
|
|
header: Padding(
|
|
padding: const EdgeInsets.fromLTRB(8, 4, 12, 0),
|
|
child: Row(
|
|
children: [
|
|
IconButton(
|
|
onPressed: sort.goToOverview,
|
|
icon: const Icon(Icons.arrow_back),
|
|
),
|
|
Expanded(
|
|
child: Text(
|
|
'Reviewing · ${pile.label}',
|
|
style: text.labelMedium,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
footer: PrimaryPillButton(
|
|
label: 'Back to overview',
|
|
onPressed: sort.goToOverview,
|
|
),
|
|
child: ListView(
|
|
children: [
|
|
Text(pile.label, style: text.displaySmall),
|
|
const SizedBox(height: 6),
|
|
Text(
|
|
'${cards.length} values in this pile. Promote toward your core or demote when something feels less essential.',
|
|
style: text.bodyMedium,
|
|
),
|
|
const SizedBox(height: 12),
|
|
SingleChildScrollView(
|
|
scrollDirection: Axis.horizontal,
|
|
child: Row(
|
|
children: ValuePile.values.map((p) {
|
|
return Padding(
|
|
padding: const EdgeInsets.only(right: 8),
|
|
child: ActionChip(
|
|
avatar: CircleAvatar(
|
|
backgroundColor: p.accent.withValues(alpha: 0.2),
|
|
radius: 8,
|
|
),
|
|
label: Text('${p.shortLabel} ${sort.countInPile(p)}'),
|
|
onPressed: () => sort.openPileReview(p),
|
|
backgroundColor: p == pile
|
|
? AppTheme.surfaceContainerHigh
|
|
: AppTheme.surfaceContainerLow,
|
|
),
|
|
);
|
|
}).toList(),
|
|
),
|
|
),
|
|
const SizedBox(height: 16),
|
|
if (cards.isEmpty)
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 40),
|
|
child: Text(
|
|
'No cards in this pile yet.',
|
|
style: text.bodyLarge?.copyWith(color: colors.onSurfaceVariant),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
)
|
|
else
|
|
...cards.map((card) {
|
|
return Padding(
|
|
padding: const EdgeInsets.only(bottom: 12),
|
|
child: ValueCardTile(
|
|
card: card,
|
|
compact: true,
|
|
trailing: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
IconButton(
|
|
tooltip: 'Promote',
|
|
onPressed: () => _shift(sort, card.id, pile, up: true),
|
|
icon: const Icon(Icons.arrow_upward),
|
|
),
|
|
IconButton(
|
|
tooltip: 'Demote',
|
|
onPressed: () =>
|
|
_shift(sort, card.id, pile, up: false),
|
|
icon: const Icon(Icons.arrow_downward),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
void _shift(
|
|
ValuesSortController sort,
|
|
String id,
|
|
ValuePile current, {
|
|
required bool up,
|
|
}) {
|
|
final values = ValuePile.values;
|
|
final index = values.indexOf(current);
|
|
final next = up ? index - 1 : index + 1;
|
|
if (next < 0 || next >= values.length) return;
|
|
sort.moveCard(id, values[next]);
|
|
}
|
|
}
|