UI Phase 2 (L0): shared layout primitives + chrome standardization
Extract four shared layout widgets from the best existing implementations and adopt them across list/overview screens, deleting duplicated inline code. Layout/consistency only — no behavior change. New primitives (lib/widgets/): - app_section_header.dart — AppSectionHeader (azure accent bar + uppercase telemetry label; promoted from the dashboard's _sectionTitle). - app_metric_card.dart — AppMetricCard (uppercase label + mono value on a flat hairline surface; unifies KPI/stat tiles). - app_status_summary.dart — AppStatusSummaryRow/Item/Chip (dot + count + label, selectable; semantic AppStatusColors). - app_filter_bar.dart — AppFilterBar (responsive filter-row chrome). Adopted / de-duplicated: - Dashboard: _MetricCard now wraps AppMetricCard; section titles use AppSectionHeader (removed local _sectionTitle). - Tickets / Tasks / ISR: three separate summary-chip implementations (_StatusSummaryCard x2, _SummaryChip) collapsed into AppStatusSummaryRow. - Network Map overview: Sites/Devices tiles -> AppMetricCard (removed local _SummaryCard). - Workforce: "Duty Schedules" header -> AppSectionHeader with trailing chip. Verified: flutter analyze lib clean; flutter build web green; visual QA (tickets + ISR summary chips) in dark. Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
@@ -18,6 +18,7 @@ import '../../theme/m3_motion.dart';
|
||||
import '../../utils/app_time.dart';
|
||||
import '../../utils/snackbar.dart';
|
||||
import '../../theme/app_status_colors.dart';
|
||||
import '../../widgets/app_status_summary.dart';
|
||||
import '../../widgets/m3_card.dart';
|
||||
import '../../widgets/mono_text.dart';
|
||||
import '../../widgets/reconnect_overlay.dart';
|
||||
@@ -535,8 +536,6 @@ class _StatusSummaryRow extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final cs = Theme.of(context).colorScheme;
|
||||
final status = AppStatusColors.of(context);
|
||||
final pending = requests
|
||||
.where((r) => r.status == ItServiceRequestStatus.pendingApproval)
|
||||
.length;
|
||||
@@ -554,88 +553,38 @@ class _StatusSummaryRow extends StatelessWidget {
|
||||
.where((r) => r.status == ItServiceRequestStatus.completed)
|
||||
.length;
|
||||
|
||||
return SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
_SummaryChip(
|
||||
label: 'Pending',
|
||||
count: pending,
|
||||
color: status.warning.color,
|
||||
selected: selectedStatus == ItServiceRequestStatus.pendingApproval,
|
||||
onTap: () => onStatusTap(ItServiceRequestStatus.pendingApproval),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
_SummaryChip(
|
||||
label: 'Scheduled',
|
||||
count: scheduled,
|
||||
color: status.info.color,
|
||||
selected: selectedStatus == ItServiceRequestStatus.scheduled,
|
||||
onTap: () => onStatusTap(ItServiceRequestStatus.scheduled),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
_SummaryChip(
|
||||
label: 'In Progress',
|
||||
count: inProgress,
|
||||
color: cs.secondary,
|
||||
selected: selectedStatus == ItServiceRequestStatus.inProgress,
|
||||
onTap: () => onStatusTap(ItServiceRequestStatus.inProgress),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
_SummaryChip(
|
||||
label: 'Completed',
|
||||
count: completed,
|
||||
color: status.success.color,
|
||||
selected: selectedStatus == ItServiceRequestStatus.completed,
|
||||
onTap: () => onStatusTap(ItServiceRequestStatus.completed),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SummaryChip extends StatelessWidget {
|
||||
const _SummaryChip({
|
||||
required this.label,
|
||||
required this.count,
|
||||
required this.color,
|
||||
required this.selected,
|
||||
required this.onTap,
|
||||
});
|
||||
|
||||
final String label;
|
||||
final int count;
|
||||
final Color color;
|
||||
final bool selected;
|
||||
final VoidCallback onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return M3Card.filled(
|
||||
onTap: onTap,
|
||||
color: selected
|
||||
? color.withValues(alpha: 0.2)
|
||||
: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
count.toString(),
|
||||
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: color,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(label, style: Theme.of(context).textTheme.bodySmall),
|
||||
],
|
||||
return AppStatusSummaryRow(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
items: [
|
||||
AppStatusSummaryItem(
|
||||
label: 'Pending',
|
||||
count: pending,
|
||||
kind: StatusKind.warning,
|
||||
selected: selectedStatus == ItServiceRequestStatus.pendingApproval,
|
||||
onTap: () => onStatusTap(ItServiceRequestStatus.pendingApproval),
|
||||
),
|
||||
),
|
||||
AppStatusSummaryItem(
|
||||
label: 'Scheduled',
|
||||
count: scheduled,
|
||||
kind: StatusKind.neutral,
|
||||
selected: selectedStatus == ItServiceRequestStatus.scheduled,
|
||||
onTap: () => onStatusTap(ItServiceRequestStatus.scheduled),
|
||||
),
|
||||
AppStatusSummaryItem(
|
||||
label: 'In Progress',
|
||||
count: inProgress,
|
||||
kind: StatusKind.info,
|
||||
selected: selectedStatus == ItServiceRequestStatus.inProgress,
|
||||
onTap: () => onStatusTap(ItServiceRequestStatus.inProgress),
|
||||
),
|
||||
AppStatusSummaryItem(
|
||||
label: 'Completed',
|
||||
count: completed,
|
||||
kind: StatusKind.success,
|
||||
selected: selectedStatus == ItServiceRequestStatus.completed,
|
||||
onTap: () => onStatusTap(ItServiceRequestStatus.completed),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user