fix(vacaciones): header Add action, info banner, dashed CTA, collapse

Audit 9b.1 (t4:446): a solid brand-teal "Add" header action -- the
prototype's own mid-page CTA (audit 9b.6, still present, now dashed)
is a SECOND, additional entry point, not a replacement.

Audit 9b.2 (t4:448): the teal explanatory banner ("alarms marked
pause-during-vacations won't ring...") is now ALWAYS visible -- never
rendered before. New ARB key vacationExplainerBanner, all 13 locales.

Audit 9b.6 (t4:487): the bottom CTA is now dashed-border with a
date_range glyph -- was a solid OutlinedButton with an add glyph.
Reuses the dashed-painter shape already established in
pantalla_favoritos.dart's custom-station CTA (audit 4.5), duplicated
rather than shared.

Audit 9b.7 (t4:489): "Rangos pasados" is now a collapsible row --
icon, title, count, chevron -- COLLAPSED by default, expanding on tap.
Was always fully expanded inline. Updated the pre-existing widget test
to tap-then-assert instead of asserting immediate visibility.

Item 9b.3 (the eyebrow literally reading "EN CURSO") is NOT
implemented: the eyebrow STYLING is already correct (audit 9b.4), and
the residual copy gap is a shared ARB string
(vacationSummaryActiveCountdown) also used compactly in
pantalla_alarmas.dart's vacation summary row -- diverging its wording
just for this screen's eyebrow, or forcing a shoutier tone into that
other compact usage, is not worth it for a trivial-rated copy nuance.
This commit is contained in:
2026-07-30 16:53:04 +02:00
parent 36d7d5f692
commit f61b0b9163
29 changed files with 414 additions and 24 deletions
+103 -1
View File
@@ -180,7 +180,8 @@ void main() {
});
testWidgets(
'los rangos pasados aparecen bajo el encabezado "Rangos pasados"',
'los rangos pasados aparecen bajo el encabezado "Rangos pasados" tras '
'expandir la fila (audit 9b.7: colapsada por defecto, t4:489)',
(tester) async {
final estado = await _crearEstado(
vacaciones: [
@@ -201,6 +202,10 @@ void main() {
tester.element(find.byType(PantallaVacaciones)),
);
expect(find.text(l10n.vacationPastSectionTitle), findsOneWidget);
await tester.tap(find.text(l10n.vacationPastSectionTitle));
await _pumpEstable(tester);
expect(find.text('Rango viejo'), findsOneWidget);
},
);
@@ -343,4 +348,101 @@ void main() {
);
},
);
group('visual fidelity (audit 9b.1/9b.2/9b.6/9b.7)', () {
testWidgets(
'9b.1: a header "Add" action is reachable and opens the same form '
'as the CTA (t4:446)',
(tester) async {
final estado = await _crearEstado();
addTearDown(estado.dispose);
await tester.pumpWidget(_buildScreen(estado));
await _pumpEstable(tester);
final l10n = AppLocalizations.of(
tester.element(find.byType(PantallaVacaciones)),
);
final appBar = tester.widget<AppBar>(find.byType(AppBar));
expect(appBar.actions, isNotNull);
expect(appBar.actions, isNotEmpty);
await tester.tap(find.byKey(const ValueKey('vacation-add-header')));
await _pumpEstable(tester);
expect(find.text(l10n.newVacationRangeTitle), findsOneWidget);
},
);
testWidgets(
'9b.2: the explanatory info banner is always visible (t4:448)',
(tester) async {
final estado = await _crearEstado();
addTearDown(estado.dispose);
await tester.pumpWidget(_buildScreen(estado));
await _pumpEstable(tester);
final l10n = AppLocalizations.of(
tester.element(find.byType(PantallaVacaciones)),
);
expect(find.text(l10n.vacationExplainerBanner), findsOneWidget);
},
);
testWidgets(
'9b.6: the bottom CTA has a dashed border and a date_range icon '
'(t4:487)',
(tester) async {
final estado = await _crearEstado();
addTearDown(estado.dispose);
await tester.pumpWidget(_buildScreen(estado));
await _pumpEstable(tester);
expect(find.byIcon(Icons.date_range_rounded), findsOneWidget);
// Exactly ONE add_rounded on the whole screen -- the header's OWN
// "Add" action (audit 9b.1). The bottom CTA no longer uses it.
expect(find.byIcon(Icons.add_rounded), findsOneWidget);
},
);
testWidgets(
'9b.7: "Rangos pasados" is collapsed by default, showing a count, '
'and expands on tap (t4:489)',
(tester) async {
final estado = await _crearEstado(
vacaciones: [
RangoVacaciones(
id: 'pa1',
nombre: 'Rango viejo',
inicio: _hoyDia.subtract(const Duration(days: 30)),
fin: _hoyDia.subtract(const Duration(days: 20)),
),
],
);
addTearDown(estado.dispose);
await tester.pumpWidget(_buildScreen(estado));
await _pumpEstable(tester);
final l10n = AppLocalizations.of(
tester.element(find.byType(PantallaVacaciones)),
);
expect(find.text(l10n.vacationPastSectionTitle), findsOneWidget);
expect(find.text('1'), findsOneWidget);
expect(
find.text('Rango viejo'),
findsNothing,
reason:
'collapsed by default -- matches the prototype count-only row',
);
await tester.tap(find.text(l10n.vacationPastSectionTitle));
await _pumpEstable(tester);
expect(find.text('Rango viejo'), findsOneWidget);
},
);
});
}